---
name: terraform-converter-bd3a081487da
description: Implement or enhance a Terraform converter for an AWS service via the spec-driven codegen pipeline. Creates a new spec + converter or adds missing resource types / fields to an existing one.
argument-hint: <service-name>
user_invocable: true
---

# Terraform Converter — Implement or Enhance

Implement or extend a Terraform converter for an AWS service using winterbaume's spec-driven codegen pipeline. Each TF resource type becomes a `[[resource]]` block in `crates/winterbaume-terraform/specs/<svc>.toml`; the `tf-converter-codegen` tool generates a serde-derived `*TfModel` struct in `crates/winterbaume-tfstate-resource-models/src/<svc>.rs`; the hand-written converter at `crates/winterbaume-terraform/src/converters/<svc>.rs` deserializes Terraform attributes via the typed model and builds the AWS-side StateView.

## Arguments

- `$0` — Service name matching the winterbaume crate suffix (e.g., `s3`, `iam`, `sqs`, `lambda`, `glue`)

---

## Architecture overview

Three files are involved per service:

```
crates/winterbaume-terraform/specs/<svc>.toml              ← hand-written, declarative
crates/winterbaume-tfstate-resource-models/src/<svc>.rs    ← AUTO-GENERATED, do not edit
crates/winterbaume-terraform/src/converters/<svc>.rs       ← hand-written, orchestrates inject/extract
```

The codegen tool is `tools/tf-converter-codegen`. It emits a `*TfModel` Rust struct per `[[resource]]` block in the spec. The `winterbaume-terraform` crate re-exports the generated crate as `crate::generated`, so converter source uses:

```rust
use crate::generated::<svc> as <svc>_gen;
let model: <svc>_gen::<Name>TfModel = serde_json::from_value(instance.attributes.clone())
    .map_err(|e| classify_deserialize_error("aws_<type>", e))?;
```

Almost every spec uses `mode = "model_only"`. Full thin-projection ( `into_state_view` and `From<&StateView>` generated for the resource ) is reserved for trivial 1:1 cases; the SQS pilot used it, but the rest of the codebase has settled on `model_only` because real AWS StateViews carry many constants, computed templates, and nested-block reshaping that the spec format can't express. **Default to `model_only`.**

---

## Step 0: Pick the work

### 0a. Is this an existing service?

```bash
ls crates/winterbaume-terraform/specs/<svc>.toml 2>/dev/null && echo EXISTS || echo NEW
```

### 0b. Check current coverage

```bash
python3 .agents/skills/api-coverage/scripts/generate_terraform_resource_coverage.py
grep -E "^\| <svc> " .agents/docs/TERRAFORM_RESOURCE_COVERAGE.md
```

The report tells you:
- How many TF resource types the service already handles.
- How many the AWS provider schema declares for the service's prefix.
- Which specific resource types are missing.

```bash
python3 -c "
import re
text = open('.agents/docs/TERRAFORM_RESOURCE_COVERAGE.md').read()
sections = re.split(r'^### ', text, flags=re.MULTILINE)
for s in sections:
    if s.startswith('<svc> '):
        print(s.rstrip()); break
"
```

If the service's prefix is `aws_<svc>_` and that prefix collides with another service ( `route53` vs `route53resolver`, `s3` vs `s3control` / `s3tables` ), the resource-coverage script's `PREFIX_OVERRIDES` table at the top of `.agents/skills/api-coverage/scripts/generate_terraform_resource_coverage.py` handles the disambiguation. Add an override entry if your service needs one.

### 0c. Identify candidate resources to add

For each candidate, verify the underlying winterbaume-`<svc>` crate has the API operations to back it ( Create/Delete/Get/List for top-level resources; Attach/Detach/Put for sub-resources ):

```bash
grep -E "^\s*async fn handle_" crates/winterbaume-<svc>/src/handlers.rs | sed -E 's/.*handle_([a-z_]+).*/\1/' | sort -u
```

And confirm the StateView has a matching collection:

```bash
grep -E "^pub struct|^    pub [a-z_]+:" crates/winterbaume-<svc>/src/views.rs | head -60
```

If the view has no collection for the resource and adding one would expand the underlying service crate, **skip the resource** and note it in your final report — it belongs to a follow-up `add-service` task, not this one. Trying to add the converter without a backing view causes the converter to be either dead code or to corrupt round-trips.

---

## Step 1: Edit the spec

### 1a. Spec file layout

`crates/winterbaume-terraform/specs/<svc>.toml`:

```toml
[service]
name = "<svc>"
crate_name = "winterbaume_<svc>"
state_view_module = "views"

[[resource]]
type = "aws_<svc>_<resource>"        # exact TF resource type name
model = "<Name>TfModel"               # generated Rust struct name
view = "<Name>View"                   # informational; matches the AWS-side view struct
[resource.modes]
mode = "model_only"

[[resource.field]]
hcl = "name"                          # TF attribute name
view = "name"                         # Rust field on the generated TfModel
                                       # If hcl != view, codegen emits #[serde(rename = "<hcl>")]
type = "string"
required = true

[[resource.field]]
hcl = "max_message_size"
view = "maximum_message_size"
type = "u32"
default = 262144

[[resource.field]]
hcl = "tags"
view = "tags"
type = "tags"                          # HashMap<String, String>
```

### 1b. Spec field-type vocabulary

The codegen accepts ONLY these types:

| Spec type | Rust type | Notes |
|-----------|-----------|-------|
| `string` | `String` | required-by-default semantics; mark `required = true` if missing-input must error |
| `string?` | `Option<String>` | always defaults to `None` |
| `u32` | `u32` | use `default = N` to set a non-zero default |
| `i64` | `i64` | same |
| `bool` | `bool` | use `default = true/false` |
| `tags` | `HashMap<String, String>` | empty map default |

Anything outside this vocabulary ( `f64`, `Vec<String>`, `Option<i64>`, `Option<bool>`, nested blocks, opaque `serde_json::Value` ) **must be dropped from the spec** and read raw in the converter via `instance.attributes.get("<key>").and_then(...)`. This is the established escape hatch ( apprunner, firehose, appflow, etc. ).

### 1c. Reserved keywords as field names

Rust reserved keywords ( `type`, `move`, `match`, etc. ) appear regularly in TF attributes. The codegen escapes them as `r#type` etc. You normally won't need to do anything; if it looks weird, look at how `ivs.toml` renames `type` → `channel_type` to avoid the `r#type` form in `view` while keeping `hcl = "type"`.

### 1d. Common spec patterns

**Field rename** ( TF `max_message_size` → view `maximum_message_size` ):
```toml
[[resource.field]]
hcl = "max_message_size"
view = "maximum_message_size"
type = "u32"
default = 262144
```

**Required field with no default**:
```toml
[[resource.field]]
hcl = "name"
view = "name"
type = "string"
required = true
```

**Optional with `unwrap_or_else` fallback in converter** ( spec is `string?`, the converter applies the fallback after deserialize ):
```toml
[[resource.field]]
hcl = "arn"
view = "arn"
type = "string?"
```

```rust
let arn = model.arn.unwrap_or_else(|| {
    format!("arn:aws:sqs:{}:{}:{}", region, ctx.default_account_id, model.name)
});
```

---

## Step 2: Regenerate the model

```bash
./.agents/bin/cargo.sh run -p tf-converter-codegen --quiet -- gen <svc>
```

This writes `crates/winterbaume-tfstate-resource-models/src/<svc>.rs`. Run `gen-all` instead if you've edited multiple specs:

```bash
./.agents/bin/cargo.sh run -p tf-converter-codegen --quiet -- gen-all
```

Generated files always have the header:

```rust
//! Auto-generated by tf-converter-codegen. Do not edit manually.
//! Source: specs/<svc>.toml
//! Regenerate with: cargo run -p tf-converter-codegen -- gen <svc>
```

A golden test verifies these files are committed in sync: `cargo test -p tf-converter-codegen` runs `check` against every spec and fails if any regenerate produces a diff. Always commit the regenerated `*<svc>.rs` alongside the spec change.

---

## Step 3: Write the converter

`crates/winterbaume-terraform/src/converters/<svc>.rs`. Established skeleton ( mirror the wave-1/2/3 IAM converters or s3 / route53 ):

```rust
//! Terraform converters for <Service> resources.

use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;

use winterbaume_core::StatefulService;
use winterbaume_<svc>::<svc>Service;
use winterbaume_<svc>::views::{<svc>StateView, <ResourceView>};
use winterbaume_tfstate::ResourceInstance;

use crate::converter::{
    ConversionContext, ConversionResult, ExtractedResource, TerraformResourceConverter,
};
use crate::error::ConversionError;
use crate::generated::<svc> as <svc>_gen;
use crate::util::{classify_deserialize_error, extract_region};

pub struct AwsXxxConverter {
    service: Arc<<svc>Service>,
}

impl AwsXxxConverter {
    pub fn new(service: Arc<<svc>Service>) -> Self {
        Self { service }
    }
}

impl TerraformResourceConverter for AwsXxxConverter {
    fn resource_type(&self) -> &str { "aws_<svc>_xxx" }

    // Optional: declare ordering dependencies for inject ( the injector
    // topo-sorts by these strings ).
    // fn depends_on_types(&self) -> Vec<&str> { vec!["aws_<svc>_parent"] }

    fn inject<'a>(
        &'a self,
        instance: &'a ResourceInstance,
        ctx: &'a ConversionContext,
    ) -> Pin<Box<dyn Future<Output = Result<ConversionResult, ConversionError>> + Send + 'a>> {
        Box::pin(async move { self.do_inject(instance, ctx).await })
    }

    fn extract<'a>(
        &'a self,
        ctx: &'a ConversionContext,
    ) -> Pin<Box<dyn Future<Output = Result<Vec<ExtractedResource>, ConversionError>> + Send + 'a>>
    {
        Box::pin(async move { self.do_extract(ctx).await })
    }
}

impl AwsXxxConverter {
    async fn do_inject(
        &self,
        instance: &ResourceInstance,
        ctx: &ConversionContext,
    ) -> Result<ConversionResult, ConversionError> {
        let region = extract_region(&instance.attributes, &ctx.default_region);
        let model: <svc>_gen::XxxTfModel = serde_json::from_value(instance.attributes.clone())
            .map_err(|e| classify_deserialize_error("aws_<svc>_xxx", e))?;

        // ARN/URL templates, constants, and any nested-block reshaping
        // happen here, NOT in the spec.
        let arn = model.arn.unwrap_or_else(|| {
            format!("arn:aws:<svc>:{}:{}:xxx/{}",
                    region, ctx.default_account_id, model.name)
        });

        // Build the AWS-side view.
        let xxx_view = XxxView {
            name: model.name.clone(),
            arn,
            tags: model.tags,
            // ...
        };

        let mut state_view = <svc>StateView::default();
        state_view.xxxs.insert(model.name, xxx_view);
        self.service.merge(&ctx.default_account_id, &region, state_view).await?;

        Ok(ConversionResult { region, warnings: vec![] })
    }

    async fn do_extract(
        &self,
        ctx: &ConversionContext,
    ) -> Result<Vec<ExtractedResource>, ConversionError> {
        let view = self.service.snapshot(&ctx.default_account_id, &ctx.default_region).await;
        let mut results = vec![];
        for xxx in view.xxxs.values() {
            let attrs = serde_json::json!({
                "id": xxx.name,
                "arn": xxx.arn,
                "name": xxx.name,
                "tags": xxx.tags,
                // ... every key from the spec's [[resource.field]] entries,
                //     plus any read-only / computed fields the TF schema
                //     declares ( tags_all, status, etc. ).
            });
            results.push(ExtractedResource {
                name: xxx.name.clone(),
                account_id: ctx.default_account_id.clone(),
                region: ctx.default_region.clone(),
                attributes: attrs,
            });
        }
        Ok(results)
    }
}
```

### Key idioms

**Sub-resource modifiers** ( `aws_iam_role_policy`, `aws_s3_bucket_versioning`, etc. ): the underlying merge is replace-not-append on the parent view, so use **snapshot+mutate+restore**:

```rust
let mut snapshot = self.service.snapshot(&ctx.default_account_id, &region).await;
if let Some(role) = snapshot.roles.get_mut(&model.role) {
    role.inline_policies.retain(|p| p.policy_name != model.name);
    role.inline_policies.push(InlinePolicyView {
        policy_name: model.name,
        policy_document: model.policy,
    });
}
self.service.restore(&ctx.default_account_id, &region, snapshot).await?;
```

Mirror `AwsIamRolePolicyConverter` in `crates/winterbaume-terraform/src/converters/iam.rs`.

**Vec<String> / nested-block raw reads**:

```rust
let users: Vec<String> = instance.attributes.get("users")
    .and_then(|v| v.as_array())
    .map(|arr| arr.iter().filter_map(|v| v.as_str().map(|s| s.to_string())).collect())
    .unwrap_or_default();

let trigger_config = instance.attributes.get("trigger_config")
    .and_then(|v| v.as_array())
    .and_then(|arr| arr.first())
    .cloned()
    .unwrap_or(serde_json::Value::Null);
```

**Nested-block shape reshape** ( the AppFlow case ): the TF state stores singleton blocks as length-1 arrays; the AWS REST JSON wants discriminated objects. The reshape lives entirely in the converter, between the model deserialize and the StateView construction — never in the spec. See `crates/winterbaume-terraform/src/converters/appflow.rs` `tf_to_aws_*` helpers.

**Tag merging** ( `tags` + `tags_all` precedence ): the existing helper `crate::util::extract_tags(attrs)` merges both ( `tags_all` first, then `tags` overrides ). Use it when the converter needs the merged result:

```rust
use crate::util::extract_tags;
let tags = extract_tags(&instance.attributes);
```

Mark the spec's `tags` field as `type = "tags"` only when you want serde to read `tags` directly into the model. If you use `extract_tags`, omit the `tags` spec field.

**ARN / URL templates**: hand-written in the converter using `format!()` with placeholders from `region`, `ctx.default_account_id`, and `model.<id_field>`. Never in the spec.

**Identifier synthesis**: when TF doesn't pass an id, synthesise via `uuid::Uuid::new_v4()` or the established `generate_id("PREFIX")` helper ( `iam.rs` ). Stable across calls is critical for waiter-friendly extracts.

---

## Step 4: Register the converter

Add the new converter struct to `crates/winterbaume-server/src/main.rs` in alphabetical order within the service's block:

```rust
injector.register(<svc>::AwsXxxConverter::new(Arc::clone(&injectable.<svc>)));
```

If the resource needs multi-scope ( regional ) handling and the service offers `scopes_with_state()`, use:

```rust
{
    let svc = Arc::clone(&injectable.<svc>);
    injector.register_with_scopes(
        <svc>::AwsXxxConverter::new(Arc::clone(&injectable.<svc>)),
        move || svc.scopes_with_state(),
    );
}
```

( Pattern in main.rs for `aws_s3_bucket` and similar account-spanning resources. )

The `winterbaume-terraform/src/converters/mod.rs` already has `pub mod <svc>;` for every service; you don't normally need to touch it. New service crates do require a new `Cargo.toml` dependency on `winterbaume-<svc>` plus a `pub mod <svc>;` line.

---

## Step 5: Verify

Per-crate gate:

```bash
./.agents/bin/cargo.sh fmt -p winterbaume-terraform 2>&1 | tail -3
./.agents/bin/cargo.sh fmt -p winterbaume-tfstate-resource-models 2>&1 | tail -3
./.agents/bin/cargo.sh clippy -p winterbaume-terraform --all-targets --all-features -- -D warnings 2>&1 | tail -5
./.agents/bin/cargo.sh clippy -p winterbaume-tfstate-resource-models --all-targets --all-features -- -D warnings 2>&1 | tail -5
./.agents/bin/cargo.sh clippy -p winterbaume-server --all-targets --all-features -- -D warnings 2>&1 | tail -5
```

Run the integration tests:

```bash
./.agents/bin/cargo.sh test -p winterbaume-terraform --no-fail-fast 2>&1 | grep -E "test result|FAILED" | tail -5
```

Expect `293+ passed; 0 failed`. Any regression in the existing 293 integration tests means your changes broke a prior converter — investigate immediately, don't push past it.

The golden test for the generated code:

```bash
./.agents/bin/cargo.sh test -p tf-converter-codegen 2>&1 | grep "test result:" | tail -3
```

The single passing case verifies every spec re-emits to byte-equal output as the committed generated file — meaning your spec edit produced exactly what's checked in. Failures here mean you forgot to run `gen <svc>` after editing the spec.

---

## Step 6: Refresh the coverage report

```bash
rm -f .agents/docs/TERRAFORM_RESOURCE_COVERAGE.md
python3 .agents/skills/api-coverage/scripts/generate_terraform_resource_coverage.py
grep -E "^\| <svc> " .agents/docs/TERRAFORM_RESOURCE_COVERAGE.md
```

Note: there's a second, complementary coverage script at
`.agents/skills/api-coverage/scripts/generate_terraform_converter_coverage.py`
that measures *per-attribute* coverage within each resource ( the inject %
and extract % thresholds — inject ≥ 60%, extract ≥ 50% for "excellent" ).
Use it when the goal is to deepen an existing resource's field coverage
rather than add new resource types.

---

## Common pitfalls

1. **`*StateView::default()` cascade.** Every `*StateView` and its component `*View` structs must `#[derive(Default)]`. Always construct with `<svc>StateView::default()` then mutate the relevant collection — never enumerate every field, because new fields on the View shape will silently get zero values from the literal construction. The bug only surfaces in the 293 integration tests.

2. **Spec format vocabulary boundary.** If you find yourself wanting to declare `Vec<String>` or `f64` in the spec, stop. Read it raw in the converter and document the omission with a TOML comment like `# <field> is Vec<String>, read raw in converter`.

3. **JSON-shape mismatch ( AppFlow case ).** TF singleton-array blocks must be reshaped before they're stored as AWS-side `serde_json::Value`. Doing the reshape in `do_inject` between the model deserialize and the StateView construction is the documented pattern. Never let the codegen 1:1-rename a nested block.

4. **`id` / `name` swap on extract.** The TF resource id and the human name are different things. For extract, `ExtractedResource.name` is the human-readable TF resource label; the `id` attribute in the emitted JSON is the AWS-side id ( ARN, UUID, etc. ). The placement-group case is the canonical bug from LTM.

5. **Multi-target resources** ( `aws_iam_policy_attachment` attaching to users + groups + roles ): extract returns `Ok(vec![])` intentionally because the resource has no view-side counterpart — the per-entity converters cover the same data. Document this with a comment.

6. **Inject-only resources** ( `aws_route` ): extract returns `Ok(vec![])`. Document.

7. **`from_value` clone hygiene.** `serde_json::from_value(instance.attributes.clone())` is the correct invocation. Skipping the clone borrows from the read-only `&ResourceInstance` and won't compile.

8. **`classify_deserialize_error`.** Always use the `crate::util::classify_deserialize_error("aws_<type>", e)` helper to map serde errors onto the existing `ConversionError::MissingAttribute` / `InvalidAttribute` variants. Don't construct error variants directly — the helper handles "missing field `x`" parsing.

9. **Reserved keyword fields.** If you use `hcl = "type"` and `view = "type"`, codegen emits `pub r#type: String` and serde rename works correctly. If you want a cleaner field name on the Rust side, use `view = "channel_type"` ( IVS pattern ).

10. **State-view literal drift.** When the underlying service crate evolves ( adds a new view field, changes a type from `Vec<X>` to `HashMap<K, X>` ), every converter constructing the view by literal `XxxView { ... }` breaks. The per-crate clippy gate doesn't catch this — only `winterbaume-terraform`'s 293 integration tests do.


---
Source attribution: moriyoshi
Original project: https://github.com/moriyoshi/winterbaume/blob/69fed1d7fb0eb9a2738ff3274197c7591129c4cb/.agents/skills/terraform-converter/SKILL.md
License: Apache-2.0
License information: https://www.apache.org/licenses/LICENSE-2.0
Original upstream instructions; downloaded from Mega AI Hub.

## Upstream license notice


                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


## Additional upstream notices

terraform-converter-bd3a081487da/DAVEAI-UPSTREAM-NOTICE

Winterbaume
Copyright 2025 Winterbaume contributors

This product includes software developed at
https://github.com/getmoto/moto

=======================================================================
Moto - Mock AWS Services
=======================================================================

Copyright 2012 Steve Pulec

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Winterbaume used moto as a reference implementation for AWS service
mock behaviour and ported many of its Python test cases into Rust
integration tests. The moto source is available at
https://github.com/getmoto/moto.

Moto is written by Steve Pulec with contributions from:

  Zach Smith, Dilshod Tadjibaev, Dan Berglund, Lincoln de Sousa,
  mhock, Ilya Sukhanov, Lucian Branescu Mihaila,
  Konstantinos Koukopoulos, attili, JJ Zeng, Jon Haddad,
  Andres Riancho, Michael Ossareh, JeffMGreg, Hugo Lopes Tavares,
  Chris St. Pierre, Frank Mata, Clint Ecker, Richard Eames,
  Kevin Glisson, Shawn Falkner-Horine, David Baumgold,
  Rory-Finnegan, Bobby Impollonia, Ralfas, Omer Katz,
  Joseph Lawson, Trey Tacon, Peter, Tyler Sanders, Gary Dalton,
  Chris Henry, Mike Fuller, Andy, Mike Grima, Marco Rucci,
  Zack Kourouma, Pior Bastida, Dustin J. Mitchell,
  Jean-Baptiste Barth, Tom Viner, Justin Wiley, Adam Stauffer,
  Guy Templeton, Michael van Tellingen, Jessie Nadler,
  Alex Morken, Clive Li, Jim Shields, William Richard,
  Alex Casalboni, Jon Beilke, Bendeguz Acs, Craig Anderson,
  Robert Lewis, Kyle Jones, Mickaël Schoentgen, Ariel Beck,
  Roman Rader, Bryan Chen, Jonas Bulik, Rita Cheung,
  David Acevedo, Damon Williams

The full contributor list is available at
https://github.com/getmoto/moto/blob/master/AUTHORS.md.

=======================================================================
AWS API Models
=======================================================================

Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Winterbaume vendors a snapshot of the official AWS API Smithy models
under vendor/api-models-aws/ to drive its in-tree wire-type and
serialiser code generation. The upstream LICENSE and NOTICE files are
preserved alongside the vendored models. The upstream source is
available at https://github.com/aws/api-models-aws.


# Mega AI Hub resource metadata

Creator / publisher: moriyoshi
Original project: https://github.com/moriyoshi/winterbaume/blob/69fed1d7fb0eb9a2738ff3274197c7591129c4cb/.agents/skills/terraform-converter/SKILL.md
Attribution basis: Source repository record

Fields of work: Development & Engineering
Roles: Backend Developer, Data Engineer, DevOps / SRE
Specialties: API, Backend
Tools: AWS, Terraform
Country applicability: Country not specified
Classification basis: Source catalog labels
Instruction SHA-256: d9cf2907eb822d7e8aaa53257a8aa920320a2537efa9a1fa936cd596460b05b5
