Skip to content

TerraformStack

laktory.models.TerraformStack ¤

Bases: BaseModel

A Terraform stack is terraform-specific flavor of the laktory.models.Stack. It re-structure the attributes to be aligned with a terraform json file.

It is generally not instantiated directly, but rather created using laktory.models.Stack.to_terraform().

METHOD DESCRIPTION
model_dump

Serialize model to match the structure of a Terraform json file.

write

Write Terraform json configuration file

init

Runs terraform init

plan

Runs terraform plan

apply

Runs terraform apply

destroy

Runs terraform destroy

Functions¤

model_dump ¤

model_dump(*args, **kwargs)

Serialize model to match the structure of a Terraform json file.

Source code in laktory/models/stacks/terraformstack.py
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
def model_dump(self, *args, **kwargs) -> dict[str, Any]:
    """Serialize model to match the structure of a Terraform json file."""
    self._configure_serializer(singular=True)
    kwargs["exclude_none"] = kwargs.get("exclude_none", True)
    d = super().model_dump(*args, **kwargs)

    # Special treatment of resources
    d["resource"] = defaultdict(lambda: {})
    d["data"] = defaultdict(lambda: {})
    for r in self.resources.values():
        if r.lookup_existing:
            d["data"][r.terraform_resource_lookup_type][r.resource_name] = (
                r.lookup_existing.model_dump()
            )
        else:
            _d = r.terraform_properties
            d["resource"][r.terraform_resource_type][r.resource_name] = _d
    d["data"] = dict(d["data"])
    if len(d["data"]) == 0:
        del d["data"]
    d["resource"] = dict(d["resource"])
    self._configure_serializer(singular=False)

    # Special treatment of moved
    # `moved_from` should generally be used with Terraform, but we also
    # support aliases (Pulumi) for better user experience
    i = -1
    for r in self.resources.values():
        moved_from = r.options.moved_from
        aliases = r.options.aliases
        _from = None
        if moved_from:
            _from = moved_from
        elif aliases:
            _from = aliases[0]
        if _from:
            i += 1
            d[f"moved_{i:05d}"] = {
                "from": f"{r.terraform_resource_type}.{_from}",
                "to": f"{r.terraform_resource_type}.{r.resource_name}",
            }

    # Terraform JSON requires the keyword "resources." to be removed and the
    # resource_name to be replaced with resource_type.resource_name.
    _vars = {}
    for r in list(self.resources.values()) + list(self.providers.values()):
        k0 = r.resource_name

        k1 = f"{r.terraform_resource_type}.{r.resource_name}"
        # special treatment for data sources
        if r.lookup_existing:
            k1 = f"data.{r.terraform_resource_lookup_type}.{r.resource_name}"

        if isinstance(r, BaseProvider):
            pattern = r"\$\{resources." + k0 + "}"
            _vars[pattern] = k0

        else:
            # ${resources.resource_name} -> resource_type.resource_name
            pattern = r"\$\{resources\." + k0 + r"\}"
            _vars[pattern] = k1

            # ${resources.resource_name.property} -> ${resource_type.resource_name.property}
            pattern = r"\$\{resources\." + k0 + r"\.(.*?)\}"
            _vars[pattern] = rf"${{{k1}.\1}}"

    # Because all variables are mapped to a string, it is more efficient
    # (>10x) to convert the dict to string before substitution.
    d = json.loads(_resolve_values(json.dumps(d), vars=_vars))

    return d

write ¤

write()

Write Terraform json configuration file

RETURNS DESCRIPTION
str

Filepath of the configuration file

Source code in laktory/models/stacks/terraformstack.py
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
def write(self) -> str:
    """
    Write Terraform json configuration file

    Returns
    -------
    :
        Filepath of the configuration file
    """
    filepath = os.path.join(CACHE_ROOT, "stack.tf.json")
    logger.info(f"Writing terraform config at '{filepath}'")

    if not os.path.exists(CACHE_ROOT):
        os.makedirs(CACHE_ROOT)

    text = json.dumps(self.model_dump(), indent=4)

    # Terraform stack file is not a strict format. Some keys might be
    # repeated and require special treatment.

    # Special treatment of providers with aliases
    for _, p in self.providers.items():
        if p.alias is not None:
            text = text.replace(
                f'"{p.resource_name}":',
                f'"{p.resource_name_without_alias}":',
            )

    # Special treatment of moved
    text = re.sub(r'"moved_\d+": {', '"moved": {', text)

    # Output
    with open(filepath, "w") as fp:
        fp.write(text)

    return filepath

init ¤

init(flags=None)

Runs terraform init

PARAMETER DESCRIPTION
flags

List of flags / options for pulumi plan

TYPE: list[str] DEFAULT: None

Source code in laktory/models/stacks/terraformstack.py
205
206
207
208
209
210
211
212
213
214
def init(self, flags: list[str] = None) -> None:
    """
    Runs `terraform init`

    Parameters
    ----------
    flags:
        List of flags / options for pulumi plan
    """
    self._call("init", flags=flags)

plan ¤

plan(flags=None)

Runs terraform plan

PARAMETER DESCRIPTION
flags

List of flags / options for pulumi plan

TYPE: list[str] DEFAULT: None

Source code in laktory/models/stacks/terraformstack.py
216
217
218
219
220
221
222
223
224
225
def plan(self, flags: list[str] = None) -> None:
    """
    Runs `terraform plan`

    Parameters
    ----------
    flags:
        List of flags / options for pulumi plan
    """
    self._call("plan", flags=flags)

apply ¤

apply(flags=None)

Runs terraform apply

PARAMETER DESCRIPTION
flags

List of flags / options for terraform apply

TYPE: list[str] DEFAULT: None

Source code in laktory/models/stacks/terraformstack.py
227
228
229
230
231
232
233
234
235
236
def apply(self, flags: list[str] = None):
    """
    Runs `terraform apply`

    Parameters
    ----------
    flags:
        List of flags / options for terraform apply
    """
    self._call("apply", flags=flags)

destroy ¤

destroy(flags=None)

Runs terraform destroy

PARAMETER DESCRIPTION
flags

List of flags / options for terraform destroy

TYPE: list[str] DEFAULT: None

Source code in laktory/models/stacks/terraformstack.py
238
239
240
241
242
243
244
245
246
247
def destroy(self, flags: list[str] = None):
    """
    Runs `terraform destroy`

    Parameters
    ----------
    flags:
        List of flags / options for terraform destroy
    """
    self._call("destroy", flags=flags)