Terraformpy是一个库和命令行工具,它使用完整的Python环境来提升您的Terraform配置!
项目描述
Terrafompy
Terraformpy是一个库和命令行工具,它使用完整的Python环境来提升您的Terraform配置!
Terraform是一个令人惊叹的工具。真的非常出色。当处理管理第三方服务定义的代码,并通过调用API实际应用这些定义的更改时,对更改过程的高度信心是必不可少的,这正是Terraform所擅长的。它所赋予的工作流程使团队能够快速在多个提供商/地区/技术等(庞大且不断增长的)范围内进行更改。
但随着您的定义增长,HCL语法很快就无法满足需求,它太冗长了……需要重复多次定义变量和输出,随着您构建更多相互使用的模块。
由于 HCL 兼容 JSON,并且 Python 在生成 JSON 数据方面表现优秀,我们开发了 Terraformpy 以提供一个更高效的环境来构建和维护复杂的 Terraform 配置。自 2016 年以来,NerdWallet(NerdWallet)已经在生产环境中每日使用 Terraformpy,并在我们整个工程组织中加速了 Terraform 的采用。
安装 Terraformpy
推荐通过 Pipenv 来安装和使用 Terraformpy。
示例:
$ mkdir my-terraform-project
$ cd my-terraform-project
$ pipenv install terraformpy
然后您可以使用 pipenv run 来运行 Terraformpy。
$ pipenv run terraformpy ...
或者,您可以使用 pipenv shell 来激活虚拟环境,这样您就不需要使用 pipenv run。本文档的其余部分假设您已经运行了 pipenv shell,并且可以直接运行 terraformpy。
使用 CLI 工具
terraformpy 命令行工具作为底层 terraform 工具的包装器。当被调用时,它将首先在当前目录中查找所有 *.tf.py 文件,使用 imp 模块加载它们,生成一个名为 main.tf.json 的文件,然后调用底层工具。
# just replace terraform in your regular workflow
terraformpy plan -out=tf.plan
# review changes...
# apply them!
# since we're going to operate on the generated plan here, we don't event need to use terraformpy anymore
terraform apply tf.plan
每个 *.tf.py 文件都使用声明性语法,使用从该库导入的对象。您不需要定义主函数,只需在模块的根目录中创建类的实例(您在这里构建的是常规 Python 代码)。由于您处于完整的 Python 环境中,您能做的事情没有限制 - 导入东西,连接到数据库等。
编写 .tf.py 文件
terraformpy 命名空间提供了一些类,它们直接映射到您在常规 .tf 文件中声明的项。要编写您的定义,只需导入这些类并开始创建它们的实例。下面是从 Terraform 入门指南 中的第一个示例。
from terraformpy import Provider, Resource
Provider(
'aws',
profile='default',
region='us-east-1'
)
Resource(
'aws_instance', 'example',
ami='ami-2757f631'
instance_type='t2.micro'
)
您可以从 terraformpy 导入的内容
Resource - https://www.terraform.io/docs/configuration/resources.html
Provider - https://www.terraform.io/docs/configuration/providers.html
Variable - https://www.terraform.io/docs/configuration/variables.html
Output - https://www.terraform.io/docs/configuration/outputs.html
Module - https://www.terraform.io/docs/configuration/modules.html
Data - https://www.terraform.io/docs/configuration/data-sources.html
Terraform - https://www.terraform.io/docs/configuration/terraform.html
查看 examples/ 目录以获取完全功能性的示例。
插值
到目前为止,我们只以匿名方式使用了 terraformpy,但 Data 和 Resource 类的返回实例提供了方便的插值属性。例如,一个常见的任务是使用 Data 类来获取远程数据
ami = Data(
'aws_ami', 'ecs_ami',
most_recent=True,
filter=[
dict(name='name', values=['\*amazon-ecs-optimized']),
dict(name='owner-alias', values=['amazon'])
]
)
Resource(
'aws_instance', 'example',
ami=ami.id,
instance_type='m4.xlarge'
)
在这里,我们简单地引用 ami 对象上的 id 属性来创建 aws_instance。在编译阶段,它将被转换为正确的语法:"${data.aws_ami.ecs_ami.id}"。
这通过在我们的Data和Resource对象上添加自定义的__getattr__函数来实现,该函数将任何不存在属性的属性访问转换为Terraform插值语法。
后端
配置后端发生在Terraform对象中。有关更多详细信息,请参阅配置Terraform后端。
以下我们使用S3后端
Terraform(
backend=dict(
s3=dict(
region="us-east-1",
bucket="terraform-tfstate-bucket",
key="terraform.tfstate",
workspace_key_prefix="my_prefix",
dynamodb_table="terraform_locks",
)
)
)
模块
由于Terraformpy为您提供了Python的全部功能,我们鼓励您在构建自己的模块化功能时使用“资源集合”(参见下一节),如果您不打算将模块共享到当前组织之外。
但是,如果您想使用预构建的现有模块,则可以使用Module对象来利用现有的HCL模块
Module(
"consul",
source="hashicorp/consul/aws",
version="0.0.5",
servers=3
)
资源集合
使用Python构建配置时的一个常见模式是想要将多个不同资源抽象为一个单一对象——这正是原生Terraform模块旨在解决的问题。在terraformpy中,我们提供了一个ResourceCollection基类,用于构建表示多个资源的对象。
您可以使用Schematics来定义字段并进行验证。
例如,在部署RDS集群时,您可能希望所有集群都有一组标准的选项。您可以使用资源集合来表示这一点
from schematics import types
from schematics.types import compound
from terraformpy import Resource, ResourceCollection
class RDSCluster(ResourceCollection):
# Defining attributes of your resource collection is like defining a Schematics Model, in fact the
# ResourceCollection class is just a specialized subclass of the Schematics Model class.
#
# Each attribute becomes a field on the collection, and can be provided as a keyword when constructing
# an instance of your collection.
#
# Validation works the same as in Schematics. You can attach validators to the fields themselves and
# also define "validate_field" functions.
name = types.StringType(required=True)
azs = compound.ListType(types.StringType, required=True)
instance_class = types.StringType(required=True, choices=('db.r3.large', ...))
# The create_resources function is invoked once the instance has been created and the kwargs provided have been
# processed against the inputs. All of the instance attributes have been converted to the values provided, so
# if you access self.name in create_resources you're accessing whatever value was provided to the instance
def create_resources(self):
self.param_group = Resource(
'aws_rds_cluster_parameter_group', '{0}_pg'.format(self.name),
family='aurora5.6',
parameter=[
{'name': 'character_set_server', 'value': 'utf8'},
{'name': 'character_set_client', 'value': 'utf8'}
]
)
self.cluster = Resource(
'aws_rds_cluster', self.name,
cluster_identifier=self.name,
availability_zones=self.azs,
database_name=self.name,
master_username='root',
master_password='password',
db_cluster_parameter_group_name=self.param_group.id
)
self.instances = Resource(
'aws_rds_cluster_instance', '{0}_instances'.format(self.name),
count=2,
identifier='{0}-${{count.index}}'.format(self.name),
cluster_identifier=self.cluster.id,
instance_class=self.instance_class
)
该定义然后可以导入并用于您的terraformpy配置中。
from modules.rds import RDSCluster
cluster1 = RDSCluster(
name='cluster1',
azs=['us-west-2a','us-west-2b','us-west-2c'],
instance_class='db.r3.large'
)
# you can then refer to the resources themselves, for interpolation, through the attrs
# i.e. cluster1.cluster.id
变体
存在于许多不同环境中的资源定义通常只在每个环境之间略有不同。为了便于定义这些差异,您可以使用变体分组。
首先创建文件夹:configs/stage/、configs/prod/、configs/shared/。在每个文件夹内部放置一个__init__.py使其成为包。
然后创建文件configs/shared/instances.py
from terraformpy import Resource
Resource(
'aws_instance', 'example',
ami=ami.id,
prod_variant=dict(
instance_type='m4.xlarge'
),
stage_variant=dict(
instance_type='t2.medium'
)
)
然后创建configs/stage/main.tf.py
from terraformpy import Variant
with Variant('stage'):
import configs.shared.instances
由于实例文件是在变体上下文中导入的,因此资源将被创建,就像它已经被定义为
from terraformpy import Resource
Resource(
'aws_instance', 'example',
ami=ami.id,
instance_type='t2.medium'
)
多个提供商
根据您的Terraform使用情况,您可能需要在某个时候使用多个提供商。要在Terraform中使用多个提供商,请使用别名定义它们,然后在资源定义中引用这些别名。
为了使这种模式更简单,您可以使用Terraformpy的Provider对象作为上下文管理器,然后在此上下文中创建的资源将自动引用该提供者别名
from terraformpy import Resource, Provider
with Provider("aws", region="us-west-2", alias="west2"):
sg = Resource('aws_security_group', 'sg', ingress=['foo'])
assert sg.provider == 'aws.west2'
使用文件内容
通常,您希望包含与Python代码位于同一目录下的文件内容,但当与terraform一起运行时,使用${file('myfile.json')}插值函数,路径将相对于编译的main.tf.json文件,而不是Python代码所在的目录。
为了帮助解决这个问题,terraformpy.helpers命名空间内提供了一个名为relative_file的函数。
from terraformpy import Resource
from terraformpy.helpers import relative_file
Resource(
'aws_iam_role', 'role_name',
name='role-name',
assume_role_policy=relative_file('role_policy.json')
)
这将生成一个定义,利用${file(...)}插值函数,并使用与定义角色的Python代码相同的目录读取role_policy.json文件。
钩子
Terraformy提供了一种“钩子”系统,允许您在编译时即时修改对象。这可以用于应用转换,以便对象的使用者不必担心Terraform JSON语法的某些特殊模式细节,尤其是“属性作为块”。
这个例子最好的是aws_security_group对象类型,它要求其ingress和egress块具有所有属性,即使它们是null。让用户输入所有不同的属性并将它们设置为None是很麻烦的,因此您可以使用我们随此发行版提供的钩子
from terraformpy.hooks.aws import install_aws_security_group_attributes_as_blocks_hook
install_aws_security_group_attributes_as_blocks_hook()
现在,用户只需要在规则中指定他们关心的属性,钩子将负责填写在最终编译的JSON中必须出现的所有可选属性
有关钩子如何工作的更多信息,请参阅test_hooks_aws.py文件以及不同对象类型上add_hook函数的行内注释
注意事项和注意事项
安全组规则和self
在创建aws_security_group_rule Resource对象时,您不能将self=True传递给对象,因为Python已经将self参数传递给构造函数。在这种情况下,您需要直接在_values中指定它
sg = Resource(
'aws_security_group_rule', 'my_rule',
_values=dict(self=True),
vpc_id=vpc.id,
...
)
开发者注意事项
运行测试
我们使用tox来运行测试。在本地开发时,您可以运行
tox
使用black格式化
我们使用black来格式化代码。要应用格式,请运行
tox -e black -- .
发布步骤
创建分支
进行更改
在VERSION文件中增加版本号,并在CHANGELOG.md文件中添加条目
打开一个PR,在PR描述中标记@NerdWalletOSS/dynamorm
一旦批准并合并到master,新版本将被推送到pypi
项目详情
下载文件
下载您平台上的文件。如果您不确定选择哪个,请了解有关安装软件包的更多信息。
源分发
构建分发
terraformpy-1.3.5.tar.gz 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | ed7d003dc4aed32273c7c0e5dbac375b662e3e0378fe740683d90aa1e58e114a |
|
MD5 | 760f3e825a0a93e46e735a76bd28d2d3 |
|
BLAKE2b-256 | d4f1330e0452ebf56b428463189db5cf83a106ff94bfdaa1f091d7c8ae6f6825 |