├── TerraformLibrary ├── __init__.py └── terraformlibrary.py ├── atest ├── testdata │ ├── multi-input │ │ ├── inputs.tfvars │ │ └── multi-input.tf │ ├── tf-error │ │ └── error.tf │ └── simple │ │ └── simple.tf ├── atest-opentofu.robot └── atest-terraform.robot ├── utest ├── testdata │ ├── multi-input │ │ ├── inputs.tfvars │ │ └── multi-input.tf │ ├── tf-error │ │ └── error.tf │ └── simple │ │ └── simple.tf └── test_terraformlibrary.py ├── .gitignore ├── pyproject.toml ├── examples └── ec2 │ ├── README.md │ ├── ec2-instance.tf │ └── verify-ec2.robot ├── .github └── workflows │ ├── publish.yml │ └── ci.yml ├── tasks.py ├── docs ├── README.md └── terraformlibrary.html ├── README.md └── LICENSE /TerraformLibrary/__init__.py: -------------------------------------------------------------------------------- 1 | from .terraformlibrary import TerraformLibrary 2 | -------------------------------------------------------------------------------- /atest/testdata/multi-input/inputs.tfvars: -------------------------------------------------------------------------------- 1 | var_one = "one" 2 | var_two = "two" 3 | var_three = "three" 4 | var_four = "four" 5 | -------------------------------------------------------------------------------- /utest/testdata/multi-input/inputs.tfvars: -------------------------------------------------------------------------------- 1 | var_one = "one" 2 | var_two = "two" 3 | var_three = "three" 4 | var_four = "four" 5 | -------------------------------------------------------------------------------- /atest/testdata/tf-error/error.tf: -------------------------------------------------------------------------------- 1 | # TF script causing an error 2 | 3 | output "out" { 4 | # Generate an error at runtime by evaluating a regex late 5 | value = regex(var.in == null ? "invalid(" : "invalid(", "input") 6 | } 7 | -------------------------------------------------------------------------------- /utest/testdata/tf-error/error.tf: -------------------------------------------------------------------------------- 1 | # TF script causing an error 2 | 3 | output "out" { 4 | # Generate an error at runtime by evaluating a regex late 5 | value = regex(var.in == null ? "invalid(" : "invalid(", "input") 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | results/ 2 | log.html 3 | report.html 4 | output.xml 5 | __pycache__/ 6 | .coverage 7 | htmlcov/ 8 | dist/ 9 | .coverage* 10 | **/.terraform 11 | .terraform.lock.hcl 12 | terraform.tfstate 13 | terraform.tfstate.backup 14 | .pytest_cache 15 | poetry.lock 16 | venv 17 | private_key.pem 18 | .env 19 | -------------------------------------------------------------------------------- /atest/testdata/simple/simple.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | local = { 4 | source = "hashicorp/local" 5 | version = "2.5.1" 6 | } 7 | } 8 | } 9 | 10 | resource "local_file" "foo" { 11 | content = "foo!" 12 | filename = "${path.module}/foo.bar" 13 | } 14 | 15 | variable "my_var" { 16 | type = string 17 | } 18 | 19 | output "my_output" { 20 | value = var.my_var 21 | } 22 | -------------------------------------------------------------------------------- /utest/testdata/simple/simple.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | local = { 4 | source = "hashicorp/local" 5 | version = "2.5.1" 6 | } 7 | } 8 | } 9 | 10 | resource "local_file" "foo" { 11 | content = "foo!" 12 | filename = "${path.module}/foo.bar" 13 | } 14 | 15 | variable "my_var" { 16 | type = string 17 | } 18 | 19 | output "my_output" { 20 | value = var.my_var 21 | } 22 | -------------------------------------------------------------------------------- /atest/testdata/multi-input/multi-input.tf: -------------------------------------------------------------------------------- 1 | variable "var_one" { 2 | type = string 3 | } 4 | variable "var_two" { 5 | type = string 6 | } 7 | variable "var_three" { 8 | type = string 9 | } 10 | variable "var_four" { 11 | type = string 12 | } 13 | 14 | output "output_one" { 15 | value = var.var_one 16 | } 17 | output "output_two" { 18 | value = var.var_two 19 | } 20 | output "output_three" { 21 | value = var.var_three 22 | } 23 | output "output_four" { 24 | value = var.var_four 25 | } 26 | -------------------------------------------------------------------------------- /utest/testdata/multi-input/multi-input.tf: -------------------------------------------------------------------------------- 1 | variable "var_one" { 2 | type = string 3 | } 4 | variable "var_two" { 5 | type = string 6 | } 7 | variable "var_three" { 8 | type = string 9 | } 10 | variable "var_four" { 11 | type = string 12 | } 13 | 14 | output "output_one" { 15 | value = var.var_one 16 | } 17 | output "output_two" { 18 | value = var.var_two 19 | } 20 | output "output_three" { 21 | value = var.var_three 22 | } 23 | output "output_four" { 24 | value = var.var_four 25 | } 26 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "robotframework-terraformlibrary" 3 | version = "0.2.0" 4 | description = "A Robotframework Library wrapping the Terraform CLI" 5 | license = "Apache-2.0" 6 | authors = ["Nils Balkow-Tychsen"] 7 | maintainers = ["Nils Balkow-Tychsen"] 8 | readme = "./docs/README.md" 9 | homepage = "https://github.com/nilsty/robotframework-terraformlibrary" 10 | packages = [ 11 | { include = "TerraformLibrary" }, 12 | ] 13 | 14 | [tool.poetry.dependencies] 15 | python = "^3.10" 16 | robotframework = ">=7" 17 | robotframework-pythonlibcore = ">=4" 18 | 19 | [tool.poetry.dev-dependencies] 20 | pytest = "*" 21 | invoke = "*" 22 | coverage = "^6.3.2" 23 | 24 | [tool.poetry.urls] 25 | "Homepage" = "https://github.com/nilsty/robotframework-terraformlibrary" 26 | 27 | [build-system] 28 | requires = ["poetry-core>=1.0.0"] 29 | build-backend = "poetry.core.masonry.api" 30 | -------------------------------------------------------------------------------- /examples/ec2/README.md: -------------------------------------------------------------------------------- 1 | # AWS EC2 instance example test 2 | 3 | This example test demonstrates the capabilities of the Robotframework Terraform Library. 4 | The test 5 | - creates an AWS EC2 instance in your AWS account 6 | - connects to the instance via SSH 7 | - extracts the hostname 8 | - verifies the hostname against the private DNS of the instances 9 | 10 | ## Requirements 11 | The following pip packages need to be installed 12 | - Robotframework 13 | - Robotframework-SSHLibrary 14 | - Robotframework-TerraformLibrary 15 | 16 | AWS access credentials are exposed to the environment. 17 | The following environment variables are set as inputs to the terraform script. 18 | - `TF_VAR_aws_ssh_key`containing the name of an SSH key pair stored in the AWS account. 19 | - `TF_VAR_subnet` containing a subnet ID of the VPC where the EC2 instance should be created in. 20 | - `TF_VAR_security_group` containing the ID of a security group allowing SSH traffic on port 22. 21 | 22 | The private key file for the SSH access to the EC2 instance. (Filename `private_key.pem`) 23 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish to PyPi 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | publish: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | id-token: write 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | with: 16 | fetch-depth: 1 17 | 18 | - name: Set up Python 3.10 19 | uses: actions/setup-python@v5 20 | with: 21 | python-version: '3.10' 22 | 23 | - name: Install and configure Poetry 24 | uses: snok/install-poetry@v1 25 | with: 26 | virtualenvs-create: true 27 | virtualenvs-in-project: false 28 | virtualenvs-path: ~/.virtualenvs 29 | installer-parallel: true 30 | 31 | - name: Cache poetry virtualenv 32 | uses: actions/cache@v4 33 | id: cache 34 | with: 35 | path: ~/.virtualenvs 36 | key: poetry-${{ hashFiles('**/poetry.lock') }} 37 | restore-keys: | 38 | poetry-${{ hashFiles('**/poetry.lock') }} 39 | 40 | - name: Install dependencies 41 | run: poetry install 42 | if: steps.cache.outputs.cache-hit != 'true' 43 | 44 | - name: Build 45 | run: poetry build 46 | 47 | - name: Publish 48 | uses: pypa/gh-action-pypi-publish@release/v1 49 | -------------------------------------------------------------------------------- /examples/ec2/ec2-instance.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | source = "hashicorp/aws" 5 | version = "~> 5.84.0" 6 | } 7 | } 8 | } 9 | 10 | provider "aws" { 11 | region = "eu-central-1" 12 | profile = "default" 13 | } 14 | 15 | resource "aws_instance" "robot_ec2_instance" { 16 | ami = "ami-0cdd6d7420844683b" 17 | instance_type = "t2.micro" 18 | key_name = var.aws_ssh_key 19 | subnet_id = var.subnet 20 | security_groups = [var.security_group] 21 | tags = { 22 | Name = "Robot-Example-Instance" 23 | } 24 | provisioner "local-exec" { 25 | command = "aws ec2 wait instance-running --profile=default --instance-ids ${self.id}" 26 | } 27 | } 28 | 29 | output "instance_public_dns" { 30 | description = "Public Ip of the EC2 instance" 31 | value = aws_instance.robot_ec2_instance.public_dns 32 | } 33 | 34 | output "instance_private_dns" { 35 | description = "Public Ip of the EC2 instance" 36 | value = aws_instance.robot_ec2_instance.private_dns 37 | } 38 | 39 | # Input Variables 40 | variable "aws_ssh_key" { 41 | description = "SSH Key stored in AWS account" 42 | type = string 43 | } 44 | 45 | variable "subnet" { 46 | description = "Subnet for EC2 Instance" 47 | type = string 48 | } 49 | 50 | variable "security_group" { 51 | description = "Security Group for EC2 Instance" 52 | type = string 53 | } -------------------------------------------------------------------------------- /atest/atest-opentofu.robot: -------------------------------------------------------------------------------- 1 | *** Settings *** 2 | Library TerraformLibrary executable=tofu 3 | 4 | *** Variables *** 5 | ${TESTDATA} ${CURDIR}/testdata 6 | 7 | *** Test Cases *** 8 | Run OpenTofu Init 9 | ${rc} ${output} Terraform Init ${TESTDATA}/simple 10 | Should Be Equal As Integers ${rc} 0 11 | Should Contain ${output} OpenTofu has been successfully initialized! 12 | 13 | Run OpenTofu Plan 14 | Set TF Var my_var test_value 15 | ${rc} ${output} Terraform Plan ${TESTDATA}/simple 16 | Should Be Equal As Integers ${rc} 0 17 | Should Contain ${output} Plan: 1 to add, 0 to change, 0 to destroy. 18 | Should Contain ${output} + my_output = "test_value" 19 | 20 | Run OpenTofu Apply 21 | ${rc} ${output} Terraform Apply ${TESTDATA}/simple 22 | Should Be Equal As Integers ${rc} 0 23 | Should Contain ${output} Apply complete! Resources: 1 added, 0 changed, 0 destroyed. 24 | Should Contain ${output} my_output = "test_value" 25 | 26 | Inspect OpenTofu State 27 | ${output} Get Terraform State ${TESTDATA}/simple 28 | Should Be Equal As Strings ${output["values"]["root_module"]["resources"][0]["name"]} foo 29 | 30 | Run OpenTofu Destroy 31 | ${rc} ${output} Terraform Destroy ${TESTDATA}/simple 32 | Should Be Equal As Integers ${rc} 0 33 | Should Contain ${output} Destroy complete! Resources: 1 destroyed. 34 | Should Contain ${output} - my_output = "test_value" -> null 35 | 36 | OpenTofu Error Is Raised 37 | ${rc} ${output} Terraform Plan ${TESTDATA}/tf-error 38 | Should Be Equal As Integers ${rc} 1 39 | Should Contain ${output} Error: Reference to undeclared input variable -------------------------------------------------------------------------------- /examples/ec2/verify-ec2.robot: -------------------------------------------------------------------------------- 1 | *** Settings *** 2 | Library SSHLibrary 3 | Library TerraformLibrary executable=tofu 4 | 5 | *** Test Cases *** 6 | Create, Verify and Destroy EC2 Instance 7 | Create EC2 Instance via Terraform 8 | Get EC2 Instance details from Terraform State 9 | Connect to EC2 10 | Get Hostname 11 | Compare private DNS with Hostname 12 | [Teardown] Destroy EC2 Instance via Terraform 13 | 14 | *** Keywords *** 15 | Create EC2 Instance via Terraform 16 | ${rc} ${output} Terraform Init examples/ec2 17 | Log ${output} 18 | IF ${rc} != 0 Fail Terraform Error:\n${output} 19 | ${rc} ${output} Terraform Apply examples/ec2 20 | Log ${output} 21 | IF ${rc} != 0 Fail Terraform Error:\n${output} 22 | 23 | Get EC2 Instance details from Terraform State 24 | ${output} Get Terraform State examples/ec2 25 | VAR ${TF_STATE} ${output} scope=TEST 26 | VAR ${EC2_PUBLIC_DNS} 27 | ... ${TF_STATE["values"]["outputs"]["instance_public_dns"]["value"]} 28 | ... scope=TEST 29 | VAR ${EC2_PRIVATE_DNS} 30 | ... ${TF_STATE["values"]["outputs"]["instance_private_dns"]["value"]} 31 | ... scope=TEST 32 | 33 | Connect to EC2 34 | Open Connection ${EC2_PUBLIC_DNS} 35 | ... timeout=30 sec 36 | Login With Public Key ec2-user examples/ec2/private_key.pem delay=10 sec 37 | 38 | Get Hostname 39 | ${output} Execute Command echo $HOSTNAME 40 | VAR ${HOSTNAME} ${output} scope=TEST 41 | 42 | Compare private DNS with Hostname 43 | Should Be Equal As Strings ${HOSTNAME} ${EC2_PRIVATE_DNS} 44 | 45 | Destroy EC2 Instance via Terraform 46 | ${rc} ${output} Terraform Destroy examples/ec2 47 | Log ${output} 48 | IF ${rc} != 0 Fail Terraform Error:\n${output} -------------------------------------------------------------------------------- /tasks.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | import subprocess 3 | from importlib.metadata import version 4 | 5 | from invoke import task 6 | 7 | from TerraformLibrary import terraformlibrary 8 | 9 | ROOT = pathlib.Path(__file__).parent.resolve().as_posix() 10 | VERSION = version("robotframework-terraformlibrary") 11 | 12 | 13 | @task 14 | def utests(context): 15 | cmd = [ 16 | "coverage", 17 | "run", 18 | "--source=TerraformLibrary", 19 | "-p", 20 | "-m", 21 | "pytest", 22 | f"{ROOT}/utest", 23 | ] 24 | subprocess.run(" ".join(cmd), shell=True, check=True) 25 | 26 | 27 | @task 28 | def atests(context): 29 | cmd = [ 30 | "coverage", 31 | "run", 32 | "--source=TerraformLibrary", 33 | "-p", 34 | "-m", 35 | "robot", 36 | "--loglevel=TRACE:DEBUG", 37 | "--outputdir=./reports", 38 | f"{ROOT}/atest", 39 | ] 40 | subprocess.run(" ".join(cmd), shell=True, check=True) 41 | 42 | 43 | @task(utests, atests) 44 | def tests(context): 45 | subprocess.run("coverage combine", shell=True, check=False) 46 | subprocess.run("coverage report", shell=True, check=False) 47 | subprocess.run("coverage html", shell=True, check=False) 48 | 49 | 50 | @task 51 | def libdoc(context): 52 | print(f"Generating libdoc for library version {VERSION}") 53 | target = f"{ROOT}/docs/terraformlibrary.html" 54 | cmd = [ 55 | "python", 56 | "-m", 57 | "robot.libdoc", 58 | "-n TerraformLibrary", 59 | f"-v {VERSION}", 60 | "TerraformLibrary", 61 | target, 62 | ] 63 | subprocess.run(" ".join(cmd), shell=True, check=False) 64 | 65 | 66 | @task 67 | def readme(context): 68 | with open(f"{ROOT}/docs/README.md", "w", encoding="utf-8") as readme: 69 | doc_string = terraformlibrary.__doc__ 70 | readme.write(str(doc_string)) 71 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | 7 | test: 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v4 12 | with: 13 | fetch-depth: 1 14 | 15 | - name: Set up Python 3.10 16 | uses: actions/setup-python@v5 17 | with: 18 | python-version: '3.10' 19 | 20 | - name: Install and configure Poetry 21 | uses: snok/install-poetry@v1 22 | with: 23 | virtualenvs-create: true 24 | virtualenvs-in-project: false 25 | virtualenvs-path: ~/.virtualenvs 26 | installer-parallel: true 27 | 28 | - name: Cache poetry virtualenv 29 | uses: actions/cache@v4 30 | id: cache 31 | with: 32 | path: ~/.virtualenvs 33 | key: poetry-${{ hashFiles('**/poetry.lock') }} 34 | restore-keys: | 35 | poetry-${{ hashFiles('**/poetry.lock') }} 36 | 37 | - name: Install dependencies 38 | run: poetry install 39 | if: steps.cache.outputs.cache-hit != 'true' 40 | 41 | - name: Install terraform 42 | uses: hashicorp/setup-terraform@v3 43 | with: 44 | terraform_version: "1.5.7" 45 | 46 | - name: Install OpenTofu 47 | uses: opentofu/setup-opentofu@v1 48 | with: 49 | tofu_wrapper: false 50 | 51 | - name: Run tests 52 | run: | 53 | poetry run invoke tests 54 | 55 | - name: Upload test results 56 | uses: actions/upload-artifact@v4 57 | if: always() 58 | with: 59 | name: reports 60 | path: ${{ github.workspace }}/reports 61 | 62 | - name: Send report to commit 63 | if: ${{ always() }} 64 | uses: joonvena/robotframework-reporter-action@v2.5 65 | with: 66 | gh_access_token: ${{ secrets.GITHUB_TOKEN }} 67 | only_summary: true 68 | failed_tests_on_top: true -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Terraform Library for Robot Framework 3 | 4 | The TerraformLibrary is a wrapper for the Hashicorp Terraform CLI 5 | 6 | With the integration of Terraform into Robot Framework, inputs can be passed from tests to terraform executions and outputs from 7 | terraform script can be used in robot tests. Commands like ``terraform init``, ``plan``, ``apply`` and ``destroy`` can be used 8 | with any terraform script. 9 | 10 | https://developer.hashicorp.com/terraform/cli 11 | 12 | --- 13 | ## Keyword Documentation 14 | [Link to the keyword documentation](https://nilsty.github.io/robotframework-terraformlibrary/terraformlibrary.html) 15 | 16 | --- 17 | ## Installation 18 | If you already have Python >= 3.8 with pip installed, you can simply run: 19 | `pip install --upgrade robotframework-terraformlibrary` 20 | 21 | --- 22 | ## Getting started 23 | Some examples how to import and use the library. 24 | 25 | ``` robotframework 26 | *** Settings *** 27 | Library TerraformLibrary 28 | 29 | *** Variables *** 30 | ${PATH_TO_TERRAFORM_SCRIPT} ${CURDIR}/terraform-script 31 | 32 | *** Test Cases *** 33 | Run Terraform Init 34 | ${rc} ${output} Terraform Init ${PATH_TO_TERRAFORM_SCRIPT} 35 | 36 | Run Terraform Plan 37 | Set TF Var my_var test_value 38 | ${rc} ${output} Terraform Plan ${PATH_TO_TERRAFORM_SCRIPT} 39 | Should Contain ${output} Plan: 1 to add, 0 to change, 0 to destroy. 40 | 41 | Run Terraform Apply 42 | ${rc} ${output} Terraform Apply ${PATH_TO_TERRAFORM_SCRIPT} 43 | Should Contain ${output} Apply complete! Resources: 1 added, 0 changed, 0 destroyed. 44 | 45 | Inspect Terraform State 46 | ${output} Get Terraform State ${PATH_TO_TERRAFORM_SCRIPT} 47 | Should Be Equal As Strings ${output["values"]["root_module"]["resources"][0]["name"]} foo 48 | 49 | Run Terraform Destroy 50 | ${rc} ${output} Terraform Destroy ${PATH_TO_TERRAFORM_SCRIPT} 51 | Should Contain ${output} Destroy complete! Resources: 1 destroyed. 52 | ``` 53 | 54 | -------------------------------------------------------------------------------- /atest/atest-terraform.robot: -------------------------------------------------------------------------------- 1 | *** Settings *** 2 | Library TerraformLibrary 3 | 4 | *** Variables *** 5 | ${TESTDATA} ${CURDIR}/testdata 6 | 7 | *** Test Cases *** 8 | Run Terraform Init 9 | ${rc} ${output} Terraform Init ${TESTDATA}/simple 10 | Should Be Equal As Integers ${rc} 0 11 | Should Contain ${output} Terraform has been successfully initialized! 12 | 13 | Run Terraform Plan 14 | Set TF Var my_var test_value 15 | ${rc} ${output} Terraform Plan ${TESTDATA}/simple 16 | Should Be Equal As Integers ${rc} 0 17 | Should Contain ${output} Plan: 1 to add, 0 to change, 0 to destroy. 18 | Should Contain ${output} + my_output = "test_value" 19 | 20 | Run Terraform Plan With Var File 21 | ${rc} ${output} Terraform Plan ${TESTDATA}/multi-input var_files=["${TESTDATA}/multi-input/inputs.tfvars"] 22 | Should Be Equal As Integers ${rc} 0 23 | Should Contain ${output} + output_one = "one" 24 | Should Contain ${output} + output_two = "two" 25 | Should Contain ${output} + output_three = "three" 26 | Should Contain ${output} + output_four = "four" 27 | 28 | Run Terraform Plan With Variable Inputs 29 | &{inputs} Create Dictionary var_one=one var_two=two var_three=three var_four=four 30 | ${rc} ${output} Terraform Plan ${TESTDATA}/multi-input vars=${inputs} 31 | Should Be Equal As Integers ${rc} 0 32 | Should Contain ${output} + output_one = "one" 33 | Should Contain ${output} + output_two = "two" 34 | Should Contain ${output} + output_three = "three" 35 | Should Contain ${output} + output_four = "four" 36 | 37 | Run Terraform Apply 38 | ${rc} ${output} Terraform Apply ${TESTDATA}/simple 39 | Should Be Equal As Integers ${rc} 0 40 | Should Contain ${output} Apply complete! Resources: 1 added, 0 changed, 0 destroyed. 41 | Should Contain ${output} my_output = "test_value" 42 | 43 | Inspect Terraform State 44 | ${output} Get Terraform State ${TESTDATA}/simple 45 | Should Be Equal As Strings ${output["values"]["root_module"]["resources"][0]["name"]} foo 46 | 47 | Run Terraform Destroy 48 | ${rc} ${output} Terraform Destroy ${TESTDATA}/simple 49 | Should Be Equal As Integers ${rc} 0 50 | Should Contain ${output} Destroy complete! Resources: 1 destroyed. 51 | Should Contain ${output} - my_output = "test_value" -> null 52 | 53 | Terraform Error Is Raised 54 | ${rc} ${output} Terraform Plan ${TESTDATA}/tf-error 55 | Should Be Equal As Integers ${rc} 1 56 | Should Contain ${output} Error: Reference to undeclared input variable -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Terraform Library for Robot Framework 2 | [![PyPI](https://img.shields.io/pypi/v/robotframework-terraformlibrary)](https://pypi.org/project/robotframework-terraformlibrary/)[![PyPi downloads](https://img.shields.io/pypi/dm/robotframework-terraformlibrary.svg)](https://pypi.python.org/pypi/robotframework-terraformlibrary) 3 | 4 | TerraformLibrary is a wrapper for the [Hashicorp Terraform CLI](https://developer.hashicorp.com/terraform/cli) 5 | 6 | The library can also be configured to run [OpenTofu](https://opentofu.org/) instead of Terraform. 7 | 8 | --- 9 | ## Keyword Documentation 10 | [Link to the keyword documentation](https://nilsty.github.io/robotframework-terraformlibrary/terraformlibrary.html) 11 | 12 | --- 13 | ## Installation 14 | If you already have Python >= 3.8 with pip installed, you can simply run: 15 | `pip install --upgrade robotframework-terraformlibrary` 16 | 17 | --- 18 | ## Getting started 19 | Some examples how to import and use the library. 20 | 21 | ``` robotframework 22 | *** Settings *** 23 | Library TerraformLibrary 24 | 25 | *** Variables *** 26 | ${PATH_TO_TERRAFORM_SCRIPT} ${CURDIR}/terraform-script 27 | 28 | *** Test Cases *** 29 | Run Terraform Init 30 | ${rc} ${output} Terraform Init ${PATH_TO_TERRAFORM_SCRIPT} 31 | 32 | Run Terraform Plan 33 | Set TF Var my_var test_value 34 | ${rc} ${output} Terraform Plan ${PATH_TO_TERRAFORM_SCRIPT} 35 | Should Contain ${output} Plan: 1 to add, 0 to change, 0 to destroy. 36 | 37 | Run Terraform Apply 38 | ${rc} ${output} Terraform Apply ${PATH_TO_TERRAFORM_SCRIPT} 39 | Should Contain ${output} Apply complete! Resources: 1 added, 0 changed, 0 destroyed. 40 | 41 | Inspect Terraform State 42 | ${output} Get Terraform State ${PATH_TO_TERRAFORM_SCRIPT} 43 | Should Be Equal As Strings ${output["values"]["root_module"]["resources"][0]["name"]} foo 44 | 45 | Run Terraform Destroy 46 | ${rc} ${output} Terraform Destroy ${PATH_TO_TERRAFORM_SCRIPT} 47 | Should Contain ${output} Destroy complete! Resources: 1 destroyed. 48 | ``` 49 | 50 | --- 51 | ## Development 52 | 53 | Install `poetry` on your system with `pip install poetry`. 54 | 55 | Then setup the current project in a virtual env for development: 56 | 57 | ``` 58 | $ poetry env use $(which python3) 59 | $ source $(poetry env info --path)/bin/activate 60 | $ poetry install 61 | ``` 62 | 63 | Run the unit tests: 64 | 65 | ``` 66 | $ poetry run invoke utests 67 | ``` 68 | 69 | Run the acceptance tests: 70 | 71 | ``` 72 | $ poetry run invoke atests 73 | ``` 74 | 75 | Run all tests: 76 | 77 | ``` 78 | $ poetry run invoke tests 79 | ``` 80 | 81 | Exit the virtualenv 82 | 83 | ``` 84 | deactivate 85 | ``` 86 | --- 87 | ## Releasing new versions to PyPi 88 | 89 | The Robotframework-TerraformLibrary is released on [PyPi.org](https://pypi.org/project/robotframework-terraformlibrary/). 90 | To release a new version of the library to PyPi, a few steps are needed. 91 | - update the version number in [pyproject.toml](pyproject.toml) 92 | - rebuild the keyword documentation with the command `poetry run libdoc` 93 | - create a pull request with the updated pyproject.toml and the keyword documentation under `docs/terraformlibrary.html` 94 | - once the pull request is merged a new github release can be created and published which will trigger a [github action](.github/workflows/publish.yml) publishing the release to PyPi via a trusted publisher setup. -------------------------------------------------------------------------------- /utest/test_terraformlibrary.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pathlib import Path 3 | 4 | import pytest 5 | 6 | from TerraformLibrary import TerraformLibrary 7 | 8 | testdata_directory = Path(__file__).parent.resolve() / "testdata" 9 | terraform = TerraformLibrary() 10 | 11 | 12 | def test_set_tf_var(): 13 | terraform.set_tf_var("test_var", "test_value") 14 | assert os.environ["TF_VAR_test_var"] == "test_value" 15 | 16 | 17 | def test_terraform_init(): 18 | rc, output = terraform.terraform_init(f"{testdata_directory}/simple") 19 | assert rc == 0 20 | assert "Terraform has been successfully initialized!" in output 21 | 22 | 23 | def test_terraform_plan(): 24 | terraform.set_tf_var("my_var", "test_value") 25 | rc, output = terraform.terraform_plan(f"{testdata_directory}/simple") 26 | assert rc == 0 27 | assert "Terraform will perform the following actions:" in output 28 | assert "Plan: 1 to add, 0 to change, 0 to destroy." in output 29 | assert '+ my_output = "test_value"' in output 30 | 31 | 32 | def test_terraform_apply(): 33 | rc, output = terraform.terraform_apply(f"{testdata_directory}/simple") 34 | assert rc == 0 35 | assert "Apply complete! Resources: 1 added, 0 changed, 0 destroyed." in output 36 | assert 'my_output = "test_value"' in output 37 | 38 | 39 | def test_get_terraform_state(): 40 | output = terraform.get_terraform_state(f"{testdata_directory}/simple") 41 | assert output["values"]["root_module"]["resources"][0]["name"] == "foo" 42 | 43 | 44 | def test_terraform_destroy(): 45 | rc, output = terraform.terraform_destroy(f"{testdata_directory}/simple") 46 | assert rc == 0 47 | assert "Destroy complete! Resources: 1 destroyed." in output 48 | assert '- my_output = "test_value" -> null' in output 49 | 50 | 51 | def test_terraform_error(): 52 | rc, output = terraform.terraform_plan(f"{testdata_directory}/tf-error") 53 | assert rc == 1 54 | assert "Error: Reference to undeclared input variable" in output 55 | 56 | 57 | def test_terraform_plan_multi_input_varfile(): 58 | """ 59 | Multiple inputs via a variable file. 60 | """ 61 | rc, output = terraform.terraform_plan( 62 | f"{testdata_directory}/multi-input", 63 | var_files=[f"{testdata_directory}/multi-input/inputs.tfvars"], 64 | ) 65 | assert rc == 0 66 | assert '+ output_one = "one"' in output 67 | assert '+ output_two = "two"' in output 68 | assert '+ output_three = "three"' in output 69 | assert '+ output_four = "four"' in output 70 | rc, output = terraform.terraform_apply( 71 | f"{testdata_directory}/multi-input", 72 | var_files=[f"{testdata_directory}/multi-input/inputs.tfvars"], 73 | ) 74 | assert rc == 0 75 | assert 'output_one = "one"' in output 76 | assert 'output_two = "two"' in output 77 | assert 'output_three = "three"' in output 78 | assert 'output_four = "four"' in output 79 | rc, output = terraform.terraform_destroy( 80 | f"{testdata_directory}/multi-input", 81 | var_files=[f"{testdata_directory}/multi-input/inputs.tfvars"], 82 | ) 83 | assert rc == 0 84 | assert '- output_one = "one" -> null' in output 85 | assert '- output_two = "two" -> null' in output 86 | assert '- output_three = "three" -> null' in output 87 | assert '- output_four = "four" -> null' in output 88 | 89 | 90 | def test_terraform_plan_multi_input_vars(): 91 | """ 92 | Multiple inputs via a command line parameters. 93 | """ 94 | inputs = dict(var_one="one", var_two="two", var_three="three", var_four="four") 95 | rc, output = terraform.terraform_plan( 96 | f"{testdata_directory}/multi-input", vars=inputs 97 | ) 98 | assert rc == 0 99 | assert '+ output_one = "one"' in output 100 | assert '+ output_two = "two"' in output 101 | assert '+ output_three = "three"' in output 102 | assert '+ output_four = "four"' in output 103 | rc, output = terraform.terraform_apply( 104 | f"{testdata_directory}/multi-input", vars=inputs 105 | ) 106 | assert rc == 0 107 | assert 'output_one = "one"' in output 108 | assert 'output_two = "two"' in output 109 | assert 'output_three = "three"' in output 110 | assert 'output_four = "four"' in output 111 | rc, output = terraform.terraform_destroy( 112 | f"{testdata_directory}/multi-input", vars=inputs 113 | ) 114 | assert rc == 0 115 | assert '- output_one = "one" -> null' in output 116 | assert '- output_two = "two" -> null' in output 117 | assert '- output_three = "three" -> null' in output 118 | assert '- output_four = "four" -> null' in output 119 | 120 | 121 | def test_lib_init_opentofu(): 122 | terraform.__init__(executable="tofu") 123 | rc, output = terraform.terraform_init(f"{testdata_directory}/simple") 124 | assert rc == 0 125 | assert "OpenTofu has been successfully initialized!" in output 126 | -------------------------------------------------------------------------------- /TerraformLibrary/terraformlibrary.py: -------------------------------------------------------------------------------- 1 | """ 2 | # Terraform Library for Robot Framework 3 | 4 | The TerraformLibrary is a wrapper for the Hashicorp Terraform CLI 5 | 6 | With the integration of Terraform into Robot Framework, inputs can be passed from tests to terraform executions and outputs from 7 | terraform script can be used in robot tests. Commands like ``terraform init``, ``plan``, ``apply`` and ``destroy`` can be used 8 | with any terraform script. 9 | 10 | https://developer.hashicorp.com/terraform/cli 11 | 12 | --- 13 | ## Keyword Documentation 14 | [Link to the keyword documentation](https://nilsty.github.io/robotframework-terraformlibrary/terraformlibrary.html) 15 | 16 | --- 17 | ## Installation 18 | If you already have Python >= 3.8 with pip installed, you can simply run: 19 | `pip install --upgrade robotframework-terraformlibrary` 20 | 21 | --- 22 | ## Getting started 23 | Some examples how to import and use the library. 24 | 25 | ``` robotframework 26 | *** Settings *** 27 | Library TerraformLibrary 28 | 29 | *** Variables *** 30 | ${PATH_TO_TERRAFORM_SCRIPT} ${CURDIR}/terraform-script 31 | 32 | *** Test Cases *** 33 | Run Terraform Init 34 | ${rc} ${output} Terraform Init ${PATH_TO_TERRAFORM_SCRIPT} 35 | 36 | Run Terraform Plan 37 | Set TF Var my_var test_value 38 | ${rc} ${output} Terraform Plan ${PATH_TO_TERRAFORM_SCRIPT} 39 | Should Contain ${output} Plan: 1 to add, 0 to change, 0 to destroy. 40 | 41 | Run Terraform Apply 42 | ${rc} ${output} Terraform Apply ${PATH_TO_TERRAFORM_SCRIPT} 43 | Should Contain ${output} Apply complete! Resources: 1 added, 0 changed, 0 destroyed. 44 | 45 | Inspect Terraform State 46 | ${output} Get Terraform State ${PATH_TO_TERRAFORM_SCRIPT} 47 | Should Be Equal As Strings ${output["values"]["root_module"]["resources"][0]["name"]} foo 48 | 49 | Run Terraform Destroy 50 | ${rc} ${output} Terraform Destroy ${PATH_TO_TERRAFORM_SCRIPT} 51 | Should Contain ${output} Destroy complete! Resources: 1 destroyed. 52 | ``` 53 | 54 | """ 55 | 56 | import json 57 | import os 58 | import subprocess 59 | from importlib.metadata import version 60 | 61 | from robot.api import logger 62 | from robot.api.deco import library 63 | 64 | try: 65 | __version__ = version("robotframework-terraformlibrary") 66 | except Exception: 67 | pass 68 | 69 | 70 | @library(scope="GLOBAL", version=__version__, auto_keywords=True) 71 | class TerraformLibrary: 72 | """ 73 | The Terraform Library is a wrapper for the Terraform CLI. 74 | 75 | With the integration of Terraform into Robot Framework, inputs can be passed from tests to terraform executions and outputs from 76 | terraform script can be used in robot tests. Commands like ``terraform init``, ``plan``, ``apply`` and ``destroy`` can be used 77 | with any terraform script. 78 | 79 | """ 80 | 81 | def __init__(self, executable="terraform"): 82 | """ 83 | The TerraformLibrary can either use the terraform executable (default) or can be configured 84 | to run OpenTofu instead by setting the executable to `tofu`. https://opentofu.org/ 85 | | ***** Settings ***** | 86 | | Library | TerraformLibrary | executable=tofu | 87 | """ 88 | self.exec = executable 89 | 90 | def _run_command(self, command: str, include_stderr: bool = False): 91 | process = subprocess.run( 92 | command, 93 | shell=True, 94 | stdout=subprocess.PIPE, 95 | stderr=(subprocess.STDOUT if include_stderr else subprocess.PIPE), 96 | ) 97 | output = process.stdout.decode() 98 | rc = process.returncode 99 | if process.stderr: 100 | logger.write("stderr = " + process.stderr.decode()) 101 | return rc, output 102 | 103 | def terraform_init(self, script_path: str): 104 | """ 105 | ``terraform init`` 106 | Initialize your terraform working directory and download any needed providers. 107 | 108 | https://developer.hashicorp.com/terraform/cli/commands/init 109 | 110 | Example: 111 | | ${rc} | ${output} | Terraform Init | ${PATH_TO_TERRAFORM_SCRIPT} | 112 | 113 | Returns the return code and the output of the terraform command. 114 | """ 115 | command = f"{self.exec} -chdir={script_path} init -no-color" 116 | rc, output = self._run_command(command, include_stderr=True) 117 | return rc, output 118 | 119 | def terraform_plan(self, script_path: str, vars: dict = {}, var_files: list = []): 120 | """ 121 | ``terraform plan`` 122 | Create the terraform plan. 123 | 124 | https://developer.hashicorp.com/terraform/cli/commands/plan 125 | 126 | Example: 127 | | ${rc} | ${output} | Terraform Plan | ${PATH_TO_TERRAFORM_SCRIPT} | 128 | 129 | Example with passing in inputs as command line aguments: 130 | | &{terraform_inputs} | Create Dictionary | input_one=value1 | input_two=value2 | 131 | | ${rc} | ${output} | Terraform Plan | ${PATH_TO_TERRAFORM_SCRIPT} | ${terraform_inputs} | 132 | 133 | Example with passing in inputs as a file: 134 | | Create File | my_inputs.tfvars | input_one = "value1" \\ninput_two = "value2" | 135 | | ${rc} | ${output} | Terraform Plan | ${PATH_TO_TERRAFORM_SCRIPT} | var_files=["my_inputs.tfvars"] | 136 | 137 | Returns the return code and the output of the terraform command. 138 | """ 139 | command = f"{self.exec} -chdir={script_path} plan -no-color -input=false" 140 | if vars: 141 | for k, v in vars.items(): 142 | command = f"{command} -var '{k}={v}'" 143 | if var_files: 144 | for file in var_files: 145 | command = f"{command} -var-file={file}" 146 | rc, output = self._run_command(command, include_stderr=True) 147 | return rc, output 148 | 149 | def terraform_apply(self, script_path: str, vars: dict = {}, var_files: list = []): 150 | """ 151 | ``terraform apply`` 152 | Applies the terraform plan and creates resources. 153 | 154 | https://developer.hashicorp.com/terraform/cli/commands/apply 155 | 156 | Example: 157 | | ${rc} | ${output} | Terraform Apply | ${PATH_TO_TERRAFORM_SCRIPT} | 158 | 159 | Example with passing in inputs as command line aguments: 160 | | &{terraform_inputs} | Create Dictionary | input_one=value1 | input_two=value2 | 161 | | ${rc} | ${output} | Terraform Apply | ${PATH_TO_TERRAFORM_SCRIPT} | ${terraform_inputs} | 162 | 163 | Example with passing in inputs as a file: 164 | | Create File | my_inputs.tfvars | input_one = "value1" \\ninput_two = "value2" | 165 | | ${rc} | ${output} | Terraform Apply | ${PATH_TO_TERRAFORM_SCRIPT} | var_files=["my_inputs.tfvars"] | 166 | 167 | Returns the return code and the output of the terraform command. 168 | """ 169 | command = f"{self.exec} -chdir={script_path} apply -auto-approve -no-color -input=false" 170 | if vars: 171 | for k, v in vars.items(): 172 | command = f"{command} -var '{k}={v}'" 173 | if var_files: 174 | for file in var_files: 175 | command = f"{command} -var-file={file}" 176 | rc, output = self._run_command(command, include_stderr=True) 177 | return rc, output 178 | 179 | def terraform_destroy( 180 | self, script_path: str, vars: dict = {}, var_files: list = [] 181 | ): 182 | """ 183 | ``terraform destroy`` 184 | Destroys the applied resources. 185 | 186 | https://developer.hashicorp.com/terraform/cli/commands/destroy 187 | 188 | Example: 189 | | ${rc} | ${output} | Terraform Destroy | ${PATH_TO_TERRAFORM_SCRIPT} | 190 | 191 | Example with passing in inputs as command line aguments: 192 | | &{terraform_inputs} | Create Dictionary | input_one=value1 | input_two=value2 | 193 | | ${rc} | ${output} | Terraform Destroy | ${PATH_TO_TERRAFORM_SCRIPT} | ${terraform_inputs} | 194 | 195 | Example with passing in inputs as a file: 196 | | Create File | my_inputs.tfvars | input_one = "value1" \\ninput_two = "value2" | 197 | | ${rc} | ${output} | Terraform Destroy | ${PATH_TO_TERRAFORM_SCRIPT} | var_files=["my_inputs.tfvars"] | 198 | 199 | Returns the return code and the output of the terraform command. 200 | """ 201 | command = f"{self.exec} -chdir={script_path} destroy -auto-approve -no-color -input=false" 202 | if vars: 203 | for k, v in vars.items(): 204 | command = f"{command} -var '{k}={v}'" 205 | if var_files: 206 | for file in var_files: 207 | command = f"{command} -var-file={file}" 208 | rc, output = self._run_command(command, include_stderr=True) 209 | return rc, output 210 | 211 | def set_tf_var(self, name: str, value: str): 212 | """ 213 | Set an environment variable with the prefix TF_VAR_. 214 | Due to the prefix this environment variables will become available to the terraform execution. 215 | 216 | https://developer.hashicorp.com/terraform/cli/config/environment-variables#tf_var_name 217 | 218 | Example: 219 | | Set TF Var | my_var_name | my var value | 220 | 221 | This will set the environment variable `TF_VAR_my_var_name`. 222 | Which will be available as an input to the terraform script as `my_var_name`. 223 | 224 | Keep in mind that environment variables are shared between threads when running tests in parallel 225 | in the same environment. It's recommended to use the `vars` or `var_files` arguments instead. 226 | """ 227 | prefix = "TF_VAR_" 228 | os.environ[f"{prefix}{name}"] = f"{value}" 229 | 230 | def get_terraform_state(self, script_path: str): 231 | """ 232 | Get Terraform State will execute the command `terraform show --json`. 233 | This will make the terraform state available as a JSON object. 234 | 235 | https://developer.hashicorp.com/terraform/cli/commands/show 236 | 237 | Example: 238 | | ${TF_state_json} | Get Terraform State | ${PATH_TO_TERRAFORM_SCRIPT} | 239 | | Should Be Equal As Strings | ${output["values"]["root_module"]["resources"][0]["name"]} | name of the first resource | 240 | 241 | """ 242 | command = f"{self.exec} -chdir={script_path} show -json" 243 | rc, output = self._run_command(command) 244 | try: 245 | output_json = json.loads(output) 246 | return output_json 247 | except: 248 | logger.warn("output not in json format") 249 | return output 250 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /docs/terraformlibrary.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |

Opening library documentation failed

20 | 32 |
33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 |
42 | 106 | 121 | 149 | 173 | 197 | 212 | 264 | 285 | 292 | 314 | 324 | 395 | 405 | 409 | 410 | 411 | --------------------------------------------------------------------------------