├── .changes ├── 2.2.0.md ├── 2.3.0.md ├── 2.3.1.md ├── 2.3.2.md ├── 2.3.3.md ├── 2.3.4.md ├── 2.3.5.md ├── 2.3.6-alpha1.md ├── 2.3.6.md ├── 2.3.7-alpha1.md ├── 2.3.7.md └── unreleased │ └── .gitkeep ├── .changie.yaml ├── .copywrite.hcl ├── .github ├── CODEOWNERS ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── Bug_Report.yml │ ├── Feature_Request.yml │ └── config.yml ├── SUPPORT.md ├── dependabot.yml ├── labeler-issue-triage.yml ├── labeler-pull-request-triage.yml ├── pull_request_template.md └── workflows │ ├── build.yml │ ├── ci-changie.yml │ ├── compliance.yml │ ├── issue-comment-triage.yml │ ├── issue-opened.yml │ ├── lock.yml │ ├── pull-request.yml │ └── test.yml ├── .gitignore ├── .golangci.yml ├── .release ├── ci.hcl ├── release-metadata.hcl ├── security-scan.hcl └── terraform-provider-cloudinit-artifacts.hcl ├── CHANGELOG.md ├── DESIGN.md ├── GNUmakefile ├── LICENSE ├── META.d └── _summary.yaml ├── README.md ├── docs ├── cdktf │ ├── python │ │ ├── data-sources │ │ │ └── config.md │ │ ├── index.md │ │ └── resources │ │ │ └── config.md │ └── typescript │ │ ├── data-sources │ │ └── config.md │ │ ├── index.md │ │ └── resources │ │ └── config.md ├── data-sources │ └── config.md ├── index.md └── resources │ └── config.md ├── examples ├── data-sources │ └── cloudinit_config │ │ ├── cloud-config.yaml │ │ ├── data-source.tf │ │ └── hello-script.sh └── resources │ └── cloudinit_config │ ├── cloud-config.yaml │ ├── hello-script.sh │ └── resource.tf ├── go.mod ├── go.sum ├── internal ├── hashcode │ ├── hashcode.go │ └── hashcode_test.go └── provider │ ├── cloudinit_config.go │ ├── data_source_cloudinit_config.go │ ├── data_source_cloudinit_config_test.go │ ├── provider.go │ ├── provider_test.go │ ├── resource_cloudinit_config.go │ └── resource_cloudinit_config_test.go ├── main.go ├── templates ├── data-sources │ └── config.md.tmpl ├── index.md.tmpl └── resources │ └── config.md.tmpl ├── terraform-registry-manifest.json ├── tools ├── go.mod ├── go.sum └── tools.go └── version └── VERSION /.changes/2.2.0.md: -------------------------------------------------------------------------------- 1 | ## 2.2.0 (February 19, 2021) 2 | 3 | Binary releases of this provider will now include the darwin-arm64 platform. This version contains no further changes. 4 | 5 | ## 2.1.0 (November 26, 2020) 6 | 7 | NEW FEATURES: 8 | 9 | * MIMEBOUNDARY can now be customised with `boundary` ([#7](https://github.com/hashicorp/terraform-provider-cloudinit/issues/7)). 10 | 11 | ## 2.0.0 (October 12, 2020) 12 | 13 | Binary releases of this provider will now include the linux-arm64 platform. 14 | 15 | BREAKING CHANGES: 16 | 17 | * Upgrade to version 2 of the Terraform Plugin SDK, which drops support for Terraform 0.11. This provider will continue to work as expected for users of Terraform 0.11, which will not download the new version. ([#3](https://github.com/hashicorp/terraform-provider-cloudinit/issues/3)) 18 | 19 | ## 1.0.0 (April 14, 2020) 20 | 21 | Initial release. This provider exposes one resource, cloudinit_config, which is identical to the template_cloudinit_config resource in terraform-provider-template. 22 | -------------------------------------------------------------------------------- /.changes/2.3.0.md: -------------------------------------------------------------------------------- 1 | ## 2.3.0 (February 22, 2023) 2 | 3 | NOTES: 4 | 5 | * provider: Rewritten to use the [`terraform-plugin-framework`](https://www.terraform.io/plugin/framework) ([#96](https://github.com/hashicorp/terraform-provider-cloudinit/issues/96)) 6 | 7 | -------------------------------------------------------------------------------- /.changes/2.3.1.md: -------------------------------------------------------------------------------- 1 | ## 2.3.1 (February 22, 2023) 2 | 3 | BUG FIXES: 4 | 5 | * cloudinit_config: Fixed handling of unknown values in `part` blocks ([#103](https://github.com/hashicorp/terraform-provider-cloudinit/issues/103)) 6 | 7 | -------------------------------------------------------------------------------- /.changes/2.3.2.md: -------------------------------------------------------------------------------- 1 | ## 2.3.2 (February 23, 2023) 2 | 3 | BUG FIXES: 4 | 5 | * cloudinit_config: Remove length validation to allow empty content string in part blocks ([#105](https://github.com/hashicorp/terraform-provider-cloudinit/issues/105)) 6 | 7 | -------------------------------------------------------------------------------- /.changes/2.3.3.md: -------------------------------------------------------------------------------- 1 | ## 2.3.3 (November 29, 2023) 2 | 3 | NOTES: 4 | 5 | * This release introduces no functional changes. It does however include dependency updates which address upstream CVEs. ([#186](https://github.com/hashicorp/terraform-provider-cloudinit/issues/186)) 6 | 7 | -------------------------------------------------------------------------------- /.changes/2.3.4.md: -------------------------------------------------------------------------------- 1 | ## 2.3.4 (April 22, 2024) 2 | 3 | NOTES: 4 | 5 | * all: This release contains no functionality changes, only the inclusion of the LICENSE file in the release archives ([#228](https://github.com/hashicorp/terraform-provider-cloudinit/issues/228)) 6 | 7 | -------------------------------------------------------------------------------- /.changes/2.3.5.md: -------------------------------------------------------------------------------- 1 | ## 2.3.5 (September 10, 2024) 2 | 3 | NOTES: 4 | 5 | * all: This release introduces no functional changes. It does however include dependency updates which address upstream CVEs. ([#263](https://github.com/hashicorp/terraform-provider-cloudinit/issues/263)) 6 | 7 | -------------------------------------------------------------------------------- /.changes/2.3.6-alpha1.md: -------------------------------------------------------------------------------- 1 | ## 2.3.6-alpha1 (December 05, 2024) 2 | 3 | NOTES: 4 | 5 | * all: This release contains no functionality changes. It is released using new build and release Actions. ([#293](https://github.com/hashicorp/terraform-provider-cloudinit/issues/293)) 6 | 7 | -------------------------------------------------------------------------------- /.changes/2.3.6.md: -------------------------------------------------------------------------------- 1 | ## 2.3.6 (February 27, 2025) 2 | 3 | NOTES: 4 | 5 | * all: This release contains no functionality changes. It is being used to fix release metadata in the registry from the previous alpha1 release. ([#318](https://github.com/hashicorp/terraform-provider-cloudinit/issues/318)) 6 | 7 | -------------------------------------------------------------------------------- /.changes/2.3.7-alpha1.md: -------------------------------------------------------------------------------- 1 | ## 2.3.7-alpha1 (March 04, 2025) 2 | 3 | NOTES: 4 | 5 | * all: This release is being used to test new build and release actions. 6 | 7 | -------------------------------------------------------------------------------- /.changes/2.3.7.md: -------------------------------------------------------------------------------- 1 | ## 2.3.7 (April 21, 2025) 2 | 3 | NOTES: 4 | 5 | * Update dependencies ([#339](https://github.com/hashicorp/terraform-provider-cloudinit/issues/339)) 6 | 7 | -------------------------------------------------------------------------------- /.changes/unreleased/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hashicorp/terraform-provider-cloudinit/d0453b832c2d51f01177f323218afe7249b9ab8e/.changes/unreleased/.gitkeep -------------------------------------------------------------------------------- /.changie.yaml: -------------------------------------------------------------------------------- 1 | # DO NOT EDIT - This GitHub Workflow is managed by automation 2 | # https://github.com/hashicorp/terraform-devex-repos 3 | changesDir: .changes 4 | unreleasedDir: unreleased 5 | changelogPath: CHANGELOG.md 6 | versionExt: md 7 | versionFormat: '## {{.Version}} ({{.Time.Format "January 02, 2006"}})' 8 | kindFormat: '{{.Kind}}:' 9 | changeFormat: '* {{.Body}} ([#{{.Custom.Issue}}](https://github.com/hashicorp/terraform-provider-cloudinit/issues/{{.Custom.Issue}}))' 10 | custom: 11 | - key: Issue 12 | label: Issue/PR Number 13 | type: int 14 | minInt: 1 15 | kinds: 16 | - label: BREAKING CHANGES 17 | - label: NOTES 18 | - label: FEATURES 19 | - label: ENHANCEMENTS 20 | - label: BUG FIXES 21 | newlines: 22 | afterKind: 1 23 | beforeKind: 1 24 | endOfVersion: 2 25 | -------------------------------------------------------------------------------- /.copywrite.hcl: -------------------------------------------------------------------------------- 1 | schema_version = 1 2 | 3 | project { 4 | license = "MPL-2.0" 5 | copyright_year = 2019 6 | 7 | header_ignore = [ 8 | # internal catalog metadata (prose) 9 | "META.d/**/*.yaml", 10 | 11 | # changie tooling configuration and CHANGELOG entries (prose) 12 | ".changes/unreleased/*.yaml", 13 | ".changie.yaml", 14 | 15 | # examples used within documentation (prose) 16 | "examples/**", 17 | 18 | # GitHub issue template configuration 19 | ".github/ISSUE_TEMPLATE/*.yml", 20 | 21 | # GitHub Actions workflow-specific configurations 22 | ".github/labeler-*.yml", 23 | 24 | # golangci-lint tooling configuration 25 | ".golangci.yml", 26 | 27 | # Release Engineering tooling configuration 28 | ".release/*.hcl", 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @hashicorp/terraform-core-plugins 2 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thank you for investing your time and energy by contributing to our project: please ensure you are familiar 4 | with the [HashiCorp Code of Conduct](https://github.com/hashicorp/.github/blob/master/CODE_OF_CONDUCT.md). 5 | 6 | This provider is a HashiCorp **utility provider**, which means any bug fix and feature 7 | has to be considered in the context of the thousands/millions of configurations in which this provider is used. 8 | This is great as your contribution can have a big positive impact, but we have to assess potential negative impact too 9 | (e.g. breaking existing configurations). _Stability over features_. 10 | 11 | To provide some safety to the wider provider ecosystem, we strictly follow 12 | [semantic versioning](https://semver.org/) and HashiCorp's own 13 | [versioning specification](https://www.terraform.io/plugin/sdkv2/best-practices/versioning#versioning-specification). 14 | Any changes that could be considered as breaking will only be included as part of a major release. 15 | In case multiple breaking changes need to happen, we will group them in the next upcoming major release. 16 | 17 | ## Asking Questions 18 | 19 | For questions, curiosity, or if still unsure what you are dealing with, 20 | please see the HashiCorp [Terraform Providers Discuss](https://discuss.hashicorp.com/c/terraform-providers/31) 21 | forum. 22 | 23 | ## Reporting Vulnerabilities 24 | 25 | Please disclose security vulnerabilities responsibly by following the 26 | [HashiCorp Vulnerability Reporting guidelines](https://www.hashicorp.com/security#vulnerability-reporting). 27 | 28 | ## Raising Issues 29 | 30 | We welcome issues of all kinds including feature requests, bug reports or documentation suggestions. 31 | Below are guidelines for well-formed issues of each type. 32 | 33 | ### Bug Reports 34 | 35 | * [ ] **Test against latest release**: Make sure you test against the latest available version of Terraform and the provider. 36 | It is possible we may have already fixed the bug you're experiencing. 37 | * [ ] **Search for duplicates**: It's helpful to keep bug reports consolidated to one thread, so do a quick search 38 | on existing bug reports to check if anybody else has reported the same thing. 39 | You can scope searches by the label `bug` to help narrow things down. 40 | * [ ] **Include steps to reproduce**: Provide steps to reproduce the issue, along with code examples and/or real code, 41 | so we can try to reproduce it. Without this, it makes it much harder (sometimes impossible) to fix the issue. 42 | 43 | ### Feature Requests 44 | 45 | * [ ] **Search for possible duplicate requests**: It's helpful to keep requests consolidated to one thread, 46 | so do a quick search on existing requests to check if anybody else has reported the same thing. 47 | You can scope searches by the label `enhancement` to help narrow things down. 48 | * [ ] **Include a use case description**: In addition to describing the behavior of the feature you'd like to see added, 49 | it's helpful to also make a case for why the feature would be important and how it would benefit 50 | the provider and, potentially, the wider Terraform ecosystem. 51 | 52 | ## New Pull Request 53 | 54 | Thank you for contributing! 55 | 56 | We are happy to review pull requests without associated issues, 57 | but we **highly recommend** starting by describing and discussing 58 | your problem or feature and attaching use cases to an issue first 59 | before raising a pull request. 60 | 61 | * [ ] **Early validation of idea and implementation plan**: provider development is complicated enough that there 62 | are often several ways to implement something, each of which has different implications and tradeoffs. 63 | Working through a plan of attack with the team before you dive into implementation will help ensure that you're 64 | working in the right direction. 65 | * [ ] **Tests**: It may go without saying, but every new patch should be covered by tests wherever possible. 66 | For bug-fixes, tests to prove the fix is valid. For features, tests to exercise the new code paths. 67 | * [ ] **Go Modules**: We use [Go Modules](https://github.com/golang/go/wiki/Modules) to manage and version our dependencies. 68 | Please make sure that you reflect dependency changes in your pull requests appropriately 69 | (e.g. `go get`, `go mod tidy` or other commands). 70 | Refer to the [dependency updates](#dependency-updates) section for more information about how 71 | this project maintains existing dependencies. 72 | * [ ] **Changelog**: Refer to the [changelog](#changelog) section for more information about how to create changelog entries. 73 | * [ ] **License Headers**: All source code requires a license header at the top of the file, refer to [License Headers](#license-headers) for information on how to autogenerate these headers. 74 | 75 | ### Dependency Updates 76 | 77 | Dependency management is performed by [Dependabot](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates). 78 | Where possible, dependency updates should occur through that system to ensure all Go module files are appropriately 79 | updated and to prevent duplicated effort of concurrent update submissions. 80 | Once available, updates are expected to be verified and merged to prevent latent technical debt. 81 | 82 | ### Changelog 83 | 84 | HashiCorp’s open-source projects have always maintained user-friendly, readable `CHANGELOG`s that allow 85 | practitioners and developers to tell at a glance whether a release should have any effect on them, 86 | and to gauge the risk of an upgrade. 87 | 88 | We follow Terraform Plugin 89 | [changelog specifications](https://www.terraform.io/plugin/sdkv2/best-practices/versioning#changelog-specification). 90 | 91 | #### Changie Automation Tool 92 | This provider uses the [Changie](https://changie.dev/) automation tool for changelog automation. 93 | To add a new entry to the `CHANGELOG` install Changie using the following [instructions](https://changie.dev/guide/installation/) 94 | and run 95 | ```bash 96 | changie new 97 | ``` 98 | then choose a `kind` of change corresponding to the Terraform Plugin [changelog categories](https://developer.hashicorp.com/terraform/plugin/sdkv2/best-practices/versioning#categorization) 99 | and then fill out the body following the entry format. Changie will then prompt for a Github issue or pull request number. 100 | Repeat this process for any additional changes. The `.yaml` files created in the `.changes/unreleased` folder 101 | should be pushed the repository along with any code changes. 102 | 103 | #### Entry format 104 | 105 | Entries that are specific to _resources_ or _data sources_, they should look like: 106 | 107 | ```markdown 108 | * resource/RESOURCE_NAME: ENTRY DESCRIPTION 109 | 110 | * data-source/DATA-SOURCE_NAME: ENTRY DESCRIPTION 111 | ``` 112 | 113 | #### Pull Request Types to `CHANGELOG` 114 | 115 | The `CHANGELOG` is intended to show developer-impacting changes to the codebase for a particular version. 116 | If every change or commit to the code resulted in an entry, the `CHANGELOG` would become less useful for developers. 117 | The lists below are general guidelines to decide whether a change should have an entry. 118 | 119 | ##### Changes that should not have a `CHANGELOG` entry 120 | 121 | * Documentation updates 122 | * Testing updates 123 | * Code refactoring 124 | 125 | ##### Changes that may have a `CHANGELOG` entry 126 | 127 | * Dependency updates: If the update contains relevant bug fixes or enhancements that affect developers, 128 | those should be called out. 129 | 130 | ##### Changes that should have a `CHANGELOG` entry 131 | 132 | * Major features 133 | * Bug fixes 134 | * Enhancements 135 | * Deprecations 136 | * Breaking changes and removals 137 | 138 | ### License Headers 139 | 140 | All source code files (excluding autogenerated files like `go.mod`, prose, and files excluded in [.copywrite.hcl](../.copywrite.hcl)) must have a license header at the top. 141 | 142 | This can be autogenerated by running `make generate` or running `go generate ./...` in the [/tools](../tools) directory. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Bug_Report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Something is incorrect or not working as expected. 3 | labels: ["bug"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thank you for taking the time to fill out this bug report! Please note that this issue tracker is only used for bug reports and feature requests. Other issues will be closed. 9 | 10 | If you have a configuration, workflow, or other question, please go back to the issue chooser and select one of the question links. 11 | - type: textarea 12 | id: versions 13 | attributes: 14 | label: Terraform CLI and Provider Versions 15 | description: What versions of Terraform CLI and the provider? 16 | placeholder: Output of `terraform version` from configuration directory 17 | validations: 18 | required: true 19 | - type: textarea 20 | id: terraform-configuration 21 | attributes: 22 | label: Terraform Configuration 23 | description: Please copy and paste any relevant Terraform configuration. This will be automatically formatted into code, so no need for backticks. 24 | render: terraform 25 | validations: 26 | required: true 27 | - type: textarea 28 | id: expected-behavior 29 | attributes: 30 | label: Expected Behavior 31 | description: What did you expect to happen? 32 | placeholder: Description of what should have happened. 33 | validations: 34 | required: true 35 | - type: textarea 36 | id: actual-behavior 37 | attributes: 38 | label: Actual Behavior 39 | description: What actually happened? 40 | placeholder: Description of what actually happened. 41 | validations: 42 | required: true 43 | - type: textarea 44 | id: reproduction-steps 45 | attributes: 46 | label: Steps to Reproduce 47 | description: List of steps to reproduce the issue. 48 | value: | 49 | 1. `terraform apply` 50 | validations: 51 | required: true 52 | - type: dropdown 53 | id: impact 54 | attributes: 55 | label: How much impact is this issue causing? 56 | description: High represents completely not able to use the provider or unexpected destruction of data/infrastructure. Medium represents unable to upgrade provider version or an issue with potential workaround. Low represents minor provider code, configuration, or documentation issues. 57 | options: 58 | - High 59 | - Medium 60 | - Low 61 | validations: 62 | required: true 63 | - type: input 64 | id: logs 65 | attributes: 66 | label: Logs 67 | description: Please provide a link to a [GitHub Gist](https://gist.github.com) containing TRACE log output. [Terraform Debugging Documentation](https://www.terraform.io/internals/debugging) 68 | placeholder: https://gist.github.com/example/12345678 69 | validations: 70 | required: false 71 | - type: textarea 72 | id: additional-information 73 | attributes: 74 | label: Additional Information 75 | description: Are there any additional details about your environment, workflow, or recent changes that might be relevant? Have you discovered a workaround? Are there links to other related issues? 76 | validations: 77 | required: false 78 | - type: checkboxes 79 | id: terms 80 | attributes: 81 | label: Code of Conduct 82 | description: By submitting this issue, you agree to follow our [Community Guidelines](https://www.hashicorp.com/community-guidelines). 83 | options: 84 | - label: I agree to follow this project's Code of Conduct 85 | required: true 86 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature_Request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Something is missing or could be improved. 3 | labels: ["enhancement"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thank you for taking the time to fill out this feature request! Please note that this issue tracker is only used for bug reports and feature requests. Other issues will be closed. 9 | 10 | If you have a configuration, workflow, or other question, please go back to the issue chooser and select one of the question links. 11 | - type: textarea 12 | id: versions 13 | attributes: 14 | label: Terraform CLI and Provider Versions 15 | description: What versions of Terraform CLI and the provider? 16 | placeholder: Output of `terraform version` from configuration directory 17 | validations: 18 | required: true 19 | - type: textarea 20 | id: use-case 21 | attributes: 22 | label: Use Cases or Problem Statement 23 | description: What use cases or problems are you trying to solve? 24 | placeholder: Description of use cases or problems. 25 | validations: 26 | required: true 27 | - type: textarea 28 | id: proposal 29 | attributes: 30 | label: Proposal 31 | description: What solutions would you prefer? 32 | placeholder: Description of proposed solutions. 33 | validations: 34 | required: true 35 | - type: dropdown 36 | id: impact 37 | attributes: 38 | label: How much impact is this issue causing? 39 | description: High represents completely not able to use the provider without this. Medium represents unable to solve a specific problem or understand something. Low represents minor provider code, configuration, or documentation issues. 40 | options: 41 | - High 42 | - Medium 43 | - Low 44 | validations: 45 | required: true 46 | - type: textarea 47 | id: additional-information 48 | attributes: 49 | label: Additional Information 50 | description: Are there any additional details about your environment, workflow, or recent changes that might be relevant? Have you discovered a workaround? Are there links to other related issues? 51 | validations: 52 | required: false 53 | - type: checkboxes 54 | id: terms 55 | attributes: 56 | label: Code of Conduct 57 | description: By submitting this issue, you agree to follow our [Community Guidelines](https://www.hashicorp.com/community-guidelines). 58 | options: 59 | - label: I agree to follow this project's Code of Conduct 60 | required: true 61 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Terraform Provider Questions 4 | url: https://discuss.hashicorp.com/c/terraform-providers/31 5 | about: GitHub issues in this repository are only intended for bug reports and feature requests. Other issues will be closed. Please ask and answer questions through the Terraform Provider section of HashiCorp Discuss. 6 | - name: Terraform Language or Workflow Questions 7 | url: https://discuss.hashicorp.com/c/terraform-core 8 | about: Please ask and answer language or workflow related questions through the Terraform Core section of HashiCorp Discuss. 9 | -------------------------------------------------------------------------------- /.github/SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Support 2 | 3 | * Project [README](../README.md) 4 | * Official [Documentation](https://registry.terraform.io/providers/hashicorp/local/latest/docs) 5 | * Providers [Discuss forums](https://discuss.hashicorp.com/c/terraform-providers/31) 6 | * Terraform [Community](https://www.terraform.io/community.html) page 7 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # See GitHub's docs for more information on this file: 2 | # https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates 3 | version: 2 4 | updates: 5 | # Maintain dependencies for Go modules 6 | - package-ecosystem: "gomod" 7 | directory: "/" 8 | schedule: 9 | # Check for updates to Go modules every weekday 10 | interval: "daily" 11 | - package-ecosystem: "gomod" 12 | directory: "/tools" 13 | schedule: 14 | interval: "daily" 15 | - package-ecosystem: "github-actions" 16 | directory: "/" 17 | schedule: 18 | interval: "daily" 19 | -------------------------------------------------------------------------------- /.github/labeler-issue-triage.yml: -------------------------------------------------------------------------------- 1 | bug: 2 | - 'panic:' 3 | crash: 4 | - 'panic:' 5 | -------------------------------------------------------------------------------- /.github/labeler-pull-request-triage.yml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - changed-files: 3 | - any-glob-to-any-file: .github/dependabot.yml 4 | - any-glob-to-any-file: go.mod 5 | - any-glob-to-any-file: go.sum 6 | documentation: 7 | - changed-files: 8 | - any-glob-to-any-file: website/**/* -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Related Issue 2 | 3 | Fixes # 4 | 5 | ## Description 6 | 7 | In plain English, describe your approach to addressing the issue linked above. For example, if you made a particular design decision, let us know why you chose this path instead of another solution. 8 | 9 | 10 | ## Rollback Plan 11 | 12 | - [ ] If a change needs to be reverted, we will roll out an update to the code within 7 days. 13 | 14 | ## Changes to Security Controls 15 | 16 | Are there any changes to security controls (access controls, encryption, logging) in this pull request? If so, explain. 17 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # This workflow builds the product for all supported platforms and uploads the resulting 2 | # binaries as Actions artifacts. The workflow also uploads a build metadata file 3 | # (metadata.json) -- and a Terraform Registry manifest file (terraform-registry-manifest.json). 4 | # 5 | # Reference: https://github.com/hashicorp/terraform-provider-crt-example/blob/main/.github/workflows/README.md 6 | 7 | name: build 8 | 9 | # We default to running this workflow on every push to every branch. 10 | # This provides fast feedback when build issues occur, so they can be 11 | # fixed prior to being merged to the main branch. 12 | 13 | on: [workflow_dispatch, push] 14 | 15 | env: 16 | PKG_NAME: "terraform-provider-cloudinit" 17 | 18 | jobs: 19 | # Detects the Go toolchain version to use for product builds. 20 | # 21 | # The implementation is inspired by envconsul -- https://go.hashi.co/get-go-version-example 22 | get-go-version: 23 | name: "Detect Go toolchain version" 24 | runs-on: ubuntu-latest 25 | outputs: 26 | go-version: ${{ steps.get-go-version.outputs.go-version }} 27 | steps: 28 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 29 | - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 30 | with: 31 | go-version-file: 'go.mod' 32 | - name: Detect Go version 33 | id: get-go-version 34 | run: | 35 | version="$(go list -f {{.GoVersion}} -m)" 36 | echo "go-version=$version" >> "$GITHUB_OUTPUT" 37 | 38 | # Parses the version/VERSION file. Reference: https://github.com/hashicorp/actions-set-product-version/blob/main/README.md 39 | # 40 | # > This action should be implemented in product repo `build.yml` files. The action is intended to grab the version 41 | # > from the version file at the beginning of the build, then passes those versions (along with metadata, where 42 | # > necessary) to any workflow jobs that need version information. 43 | set-product-version: 44 | name: "Parse version file" 45 | runs-on: ubuntu-latest 46 | outputs: 47 | product-version: ${{ steps.set-product-version.outputs.product-version }} 48 | product-base-version: ${{ steps.set-product-version.outputs.base-product-version }} 49 | product-prerelease-version: ${{ steps.set-product-version.outputs.prerelease-product-version }} 50 | product-minor-version: ${{ steps.set-product-version.outputs.minor-product-version }} 51 | steps: 52 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 53 | - name: Set variables 54 | id: set-product-version 55 | uses: hashicorp/actions-set-product-version@v2 56 | 57 | # Creates metadata.json file containing build metadata for consumption by CRT workflows. 58 | # 59 | # Reference: https://github.com/hashicorp/actions-generate-metadata/blob/main/README.md 60 | generate-metadata-file: 61 | needs: set-product-version 62 | runs-on: ubuntu-latest 63 | outputs: 64 | filepath: ${{ steps.generate-metadata-file.outputs.filepath }} 65 | steps: 66 | - name: "Checkout directory" 67 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 68 | - name: Generate metadata file 69 | id: generate-metadata-file 70 | uses: hashicorp/actions-generate-metadata@v1 71 | with: 72 | version: ${{ needs.set-product-version.outputs.product-version }} 73 | product: ${{ env.PKG_NAME }} 74 | repositoryOwner: "hashicorp" 75 | - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 76 | with: 77 | name: metadata.json 78 | path: ${{ steps.generate-metadata-file.outputs.filepath }} 79 | 80 | # Uploads an Actions artifact named terraform-registry-manifest.json.zip. 81 | # 82 | # The artifact contains a single file with a filename that Terraform Registry expects 83 | # (example: terraform-provider-cloudinit_2.3.6-alpha1_manifest.json). The file contents 84 | # are identical to the terraform-registry-manifest.json file in the source repository. 85 | upload-terraform-registry-manifest-artifact: 86 | needs: set-product-version 87 | runs-on: ubuntu-latest 88 | steps: 89 | - name: "Checkout directory" 90 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 91 | with: 92 | path: ${{ env.PKG_NAME }} 93 | - name: "Copy manifest from checkout directory to a file with the desired name" 94 | id: terraform-registry-manifest 95 | run: | 96 | name="${{ env.PKG_NAME }}" 97 | version="${{ needs.set-product-version.outputs.product-version }}" 98 | 99 | source="${name}/terraform-registry-manifest.json" 100 | destination="${name}_${version}_manifest.json" 101 | 102 | cp "$source" "$destination" 103 | echo "filename=$destination" >> "$GITHUB_OUTPUT" 104 | - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 105 | with: 106 | name: terraform-registry-manifest.json 107 | path: ${{ steps.terraform-registry-manifest.outputs.filename }} 108 | if-no-files-found: error 109 | 110 | # Builds the product for all platforms except macOS. 111 | # 112 | # With `reproducible: report`, this job also reports whether the build is reproducible, 113 | # but does not enforce it. 114 | # 115 | # Reference: https://github.com/hashicorp/actions-go-build/blob/main/README.md 116 | build: 117 | needs: 118 | - get-go-version 119 | - set-product-version 120 | runs-on: ubuntu-latest 121 | strategy: 122 | fail-fast: true 123 | matrix: 124 | goos: [freebsd, windows, linux, darwin] 125 | goarch: ["386", "amd64", "arm", "arm64"] 126 | exclude: 127 | - goos: freebsd 128 | goarch: arm64 129 | - goos: windows 130 | goarch: arm64 131 | - goos: windows 132 | goarch: arm 133 | - goos: darwin 134 | goarch: 386 135 | - goos: darwin 136 | goarch: arm 137 | 138 | name: Go ${{ needs.get-go-version.outputs.go-version }} ${{ matrix.goos }} ${{ matrix.goarch }} build 139 | steps: 140 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 141 | - uses: hashicorp/actions-go-build@v1 142 | env: 143 | CGO_ENABLED: 0 144 | BASE_VERSION: ${{ needs.set-product-version.outputs.product-base-version }} 145 | PRERELEASE_VERSION: ${{ needs.set-product-version.outputs.product-prerelease-version}} 146 | METADATA_VERSION: ${{ env.METADATA }} 147 | with: 148 | bin_name: "${{ env.PKG_NAME }}_v${{ needs.set-product-version.outputs.product-version }}_x5" 149 | product_name: ${{ env.PKG_NAME }} 150 | product_version: ${{ needs.set-product-version.outputs.product-version }} 151 | go_version: ${{ needs.get-go-version.outputs.go-version }} 152 | os: ${{ matrix.goos }} 153 | arch: ${{ matrix.goarch }} 154 | reproducible: report 155 | instructions: | 156 | go build \ 157 | -o "$BIN_PATH" \ 158 | -trimpath \ 159 | -buildvcs=false \ 160 | -ldflags "-s -w" 161 | cp LICENSE "$TARGET_DIR/LICENSE.txt" 162 | 163 | whats-next: 164 | needs: 165 | - build 166 | - generate-metadata-file 167 | - upload-terraform-registry-manifest-artifact 168 | runs-on: ubuntu-latest 169 | name: "What's next?" 170 | steps: 171 | - name: "Write a helpful summary" 172 | run: | 173 | github_dot_com="${{ github.server_url }}" 174 | owner_with_name="${{ github.repository }}" 175 | ref="${{ github.ref }}" 176 | 177 | echo "### What's next?" >> "$GITHUB_STEP_SUMMARY" 178 | echo "#### For a release branch (see \`.release/ci.hcl\`)" >> $GITHUB_STEP_SUMMARY 179 | echo "After this \`build\` workflow run completes succesfully, you can expect the CRT \`prepare\` workflow to begin momentarily." >> "$GITHUB_STEP_SUMMARY" 180 | echo "To find the \`prepare\` workflow run, [view the checks for this commit]($github_dot_com/$owner_with_name/commits/$ref)" >> "$GITHUB_STEP_SUMMARY" 181 | -------------------------------------------------------------------------------- /.github/workflows/ci-changie.yml: -------------------------------------------------------------------------------- 1 | # DO NOT EDIT - This GitHub Workflow is managed by automation 2 | # https://github.com/hashicorp/terraform-devex-repos 3 | 4 | # Continuous integration handling for changie 5 | name: ci-changie 6 | 7 | on: 8 | pull_request: 9 | paths: 10 | - .changes/unreleased/*.yaml 11 | - .changie.yaml 12 | - .github/workflows/ci-changie.yml 13 | 14 | permissions: 15 | contents: read 16 | 17 | jobs: 18 | check: 19 | runs-on: ubuntu-latest 20 | steps: 21 | # Ensure terraform-devex-repos is updated on version changes. 22 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 23 | # Ensure terraform-devex-repos is updated on version changes. 24 | - uses: miniscruff/changie-action@6dcc2533cac0495148ed4046c438487e4dceaa23 # v2.0.0 25 | with: 26 | version: latest 27 | args: batch patch --dry-run 28 | -------------------------------------------------------------------------------- /.github/workflows/compliance.yml: -------------------------------------------------------------------------------- 1 | name: compliance 2 | 3 | on: 4 | pull_request: 5 | 6 | permissions: 7 | contents: read 8 | 9 | jobs: 10 | # Reference: ENGSRV-059 11 | copywrite: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 15 | - uses: hashicorp/setup-copywrite@32638da2d4e81d56a0764aa1547882fc4d209636 # v1.1.3 16 | - run: copywrite headers --plan 17 | - run: copywrite license --plan 18 | -------------------------------------------------------------------------------- /.github/workflows/issue-comment-triage.yml: -------------------------------------------------------------------------------- 1 | # DO NOT EDIT - This GitHub Workflow is managed by automation 2 | # https://github.com/hashicorp/terraform-devex-repos 3 | name: Issue Comment Triage 4 | 5 | on: 6 | issue_comment: 7 | types: [created] 8 | 9 | jobs: 10 | issue_comment_triage: 11 | runs-on: ubuntu-latest 12 | env: 13 | # issue_comment events are triggered by comments on issues and pull requests. Checking the 14 | # value of github.event.issue.pull_request tells us whether the issue is an issue or is 15 | # actually a pull request, allowing us to dynamically set the gh subcommand: 16 | # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#issue_comment-on-issues-only-or-pull-requests-only 17 | COMMAND: ${{ github.event.issue.pull_request && 'pr' || 'issue' }} 18 | GH_TOKEN: ${{ github.token }} 19 | steps: 20 | - name: 'Remove waiting-response on comment' 21 | run: gh ${{ env.COMMAND }} edit ${{ github.event.issue.html_url }} --remove-label waiting-response 22 | -------------------------------------------------------------------------------- /.github/workflows/issue-opened.yml: -------------------------------------------------------------------------------- 1 | name: Issue Opened Triage 2 | 3 | on: 4 | issues: 5 | types: [opened] 6 | 7 | jobs: 8 | issue_triage: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 12 | - uses: github/issue-labeler@c1b0f9f52a63158c4adc09425e858e87b32e9685 # v3.4 13 | with: 14 | repo-token: "${{ secrets.GITHUB_TOKEN }}" 15 | configuration-path: .github/labeler-issue-triage.yml 16 | enable-versioned-regex: 0 17 | -------------------------------------------------------------------------------- /.github/workflows/lock.yml: -------------------------------------------------------------------------------- 1 | # DO NOT EDIT - This GitHub Workflow is managed by automation 2 | # https://github.com/hashicorp/terraform-devex-repos 3 | name: 'Lock Threads' 4 | 5 | on: 6 | schedule: 7 | - cron: '9 1 * * *' 8 | 9 | jobs: 10 | lock: 11 | runs-on: ubuntu-latest 12 | steps: 13 | # NOTE: When TSCCR updates the GitHub action version, update the template workflow file to avoid drift: 14 | # https://github.com/hashicorp/terraform-devex-repos/blob/main/modules/repo/workflows/lock.tftpl 15 | - uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5.0.1 16 | with: 17 | github-token: ${{ github.token }} 18 | issue-inactive-days: '30' 19 | issue-lock-reason: resolved 20 | pr-inactive-days: '30' 21 | pr-lock-reason: resolved 22 | -------------------------------------------------------------------------------- /.github/workflows/pull-request.yml: -------------------------------------------------------------------------------- 1 | name: "Pull Request Triage" 2 | 3 | on: [pull_request_target] 4 | 5 | permissions: 6 | # CodelyTV/pr-size-labeler uses issues URL for labeling 7 | issues: write 8 | pull-requests: write 9 | 10 | jobs: 11 | triage: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5.0.0 15 | with: 16 | configuration-path: .github/labeler-pull-request-triage.yml 17 | repo-token: "${{ secrets.GITHUB_TOKEN }}" 18 | - uses: CodelyTV/pr-size-labeler@4ec67706cd878fbc1c8db0a5dcd28b6bb412e85a # v1.10.3 19 | with: 20 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 21 | xs_label: 'size/XS' 22 | xs_max_size: '30' 23 | s_label: 'size/S' 24 | s_max_size: '60' 25 | m_label: 'size/M' 26 | m_max_size: '150' 27 | l_label: 'size/L' 28 | l_max_size: '300' 29 | xl_label: 'size/XL' 30 | message_if_xl: '' 31 | files_to_ignore: 'go.sum' 32 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | pull_request: 5 | branches: [ main ] 6 | paths-ignore: 7 | - 'README.md' 8 | - 'CHANGELOG.md' 9 | - 'website/*' 10 | push: 11 | branches: [ main ] 12 | paths-ignore: 13 | - 'README.md' 14 | - 'CHANGELOG.md' 15 | - 'website/*' 16 | 17 | jobs: 18 | 19 | build: 20 | name: Build 21 | runs-on: ubuntu-latest 22 | timeout-minutes: 5 23 | 24 | steps: 25 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 26 | - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 27 | with: 28 | go-version-file: 'go.mod' 29 | 30 | - name: Run linters 31 | uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0 32 | with: 33 | version: latest 34 | 35 | # We need the latest version of Terraform for our documentation generation to use 36 | - name: Set up Terraform 37 | uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2 38 | with: 39 | terraform_wrapper: false 40 | 41 | - name: Generate 42 | run: make generate 43 | 44 | - name: Confirm no diff 45 | run: | 46 | git diff --compact-summary --exit-code || \ 47 | (echo "*** Unexpected differences after code generation. Run 'make generate' and commit."; exit 1) 48 | 49 | - name: Build 50 | run: make build 51 | 52 | test: 53 | name: 'Acc. Tests (OS: ${{ matrix.os }} / TF: ${{ matrix.terraform }})' 54 | needs: build 55 | runs-on: ${{ matrix.os }} 56 | timeout-minutes: 15 57 | 58 | strategy: 59 | fail-fast: false 60 | matrix: 61 | os: 62 | - macos-latest 63 | - windows-latest 64 | - ubuntu-latest 65 | terraform: ${{ fromJSON(vars.TF_VERSIONS_PROTOCOL_V5) }} 66 | 67 | steps: 68 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 69 | - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 70 | with: 71 | go-version-file: 'go.mod' 72 | 73 | - name: Setup Terraform ${{ matrix.terraform }} 74 | uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2 75 | with: 76 | terraform_version: ${{ matrix.terraform }} 77 | terraform_wrapper: false 78 | 79 | - name: Run acceptance test 80 | run: make testacc -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.dll 2 | *.exe 3 | .DS_Store 4 | example.tf 5 | terraform.tfplan 6 | terraform.tfstate 7 | bin/ 8 | modules-dev/ 9 | /pkg/ 10 | website/.vagrant 11 | website/.bundle 12 | website/build 13 | website/node_modules 14 | .vagrant/ 15 | *.backup 16 | ./*.tfstate 17 | .terraform/ 18 | *.log 19 | *.bak 20 | *~ 21 | .*.swp 22 | .idea 23 | *.iml 24 | *.test 25 | *.iml 26 | 27 | website/vendor 28 | 29 | # Test exclusions 30 | !command/test-fixtures/**/*.tfstate 31 | !command/test-fixtures/**/.terraform/ 32 | 33 | # Keep windows files with windows line endings 34 | *.winfile eol=crlf 35 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | linters: 3 | default: none 4 | enable: 5 | - copyloopvar 6 | - durationcheck 7 | - errcheck 8 | - forcetypeassert 9 | - godot 10 | - govet 11 | - ineffassign 12 | - makezero 13 | - misspell 14 | - nilerr 15 | - predeclared 16 | - staticcheck 17 | - unconvert 18 | - unparam 19 | - unused 20 | - usetesting 21 | exclusions: 22 | generated: lax 23 | presets: 24 | - comments 25 | - common-false-positives 26 | - legacy 27 | - std-error-handling 28 | paths: 29 | - third_party$ 30 | - builtin$ 31 | - examples$ 32 | settings: 33 | staticcheck: 34 | checks: 35 | - all 36 | - '-QF1012' # "Use fmt.Fprintf(...) instead of Write" -- https://staticcheck.io/docs/checks#QF1012 37 | 38 | issues: 39 | max-issues-per-linter: 0 40 | max-same-issues: 0 41 | formatters: 42 | enable: 43 | - gofmt 44 | exclusions: 45 | generated: lax 46 | paths: 47 | - third_party$ 48 | - builtin$ 49 | - examples$ 50 | -------------------------------------------------------------------------------- /.release/ci.hcl: -------------------------------------------------------------------------------- 1 | # Reference: https://github.com/hashicorp/crt-core-helloworld/blob/main/.release/ci.hcl (private repository) 2 | # 3 | # One way to validate, with a local build of the orchestrator (an internal repo): 4 | # 5 | # $ GITHUB_TOKEN="not-used" orchestrator parse config -use-v2 -local-config=.release/ci.hcl 6 | 7 | schema = "2" 8 | 9 | project "terraform-provider-cloudinit" { 10 | // team is currently unused and has no meaning 11 | // but is required to be non-empty by CRT orchestator 12 | team = "_UNUSED_" 13 | 14 | slack { 15 | notification_channel = "C02BASDVCDT" // #feed-terraform-sdk 16 | } 17 | 18 | github { 19 | organization = "hashicorp" 20 | repository = "terraform-provider-cloudinit" 21 | release_branches = ["main", "release/**"] 22 | } 23 | } 24 | 25 | event "merge" { 26 | } 27 | 28 | event "build" { 29 | action "build" { 30 | depends = ["merge"] 31 | 32 | organization = "hashicorp" 33 | repository = "terraform-provider-cloudinit" 34 | workflow = "build" 35 | } 36 | } 37 | 38 | event "prepare" { 39 | # `prepare` is the Common Release Tooling (CRT) artifact processing workflow. 40 | # It prepares artifacts for potential promotion to staging and production. 41 | # For example, it scans and signs artifacts. 42 | 43 | depends = ["build"] 44 | 45 | action "prepare" { 46 | organization = "hashicorp" 47 | repository = "crt-workflows-common" 48 | workflow = "prepare" 49 | depends = ["build"] 50 | } 51 | 52 | notification { 53 | on = "fail" 54 | } 55 | } 56 | 57 | event "trigger-staging" { 58 | } 59 | 60 | event "promote-staging" { 61 | action "promote-staging" { 62 | organization = "hashicorp" 63 | repository = "crt-workflows-common" 64 | workflow = "promote-staging" 65 | depends = null 66 | config = "release-metadata.hcl" 67 | } 68 | 69 | depends = ["trigger-staging"] 70 | 71 | notification { 72 | on = "always" 73 | } 74 | } 75 | 76 | event "trigger-production" { 77 | } 78 | 79 | event "promote-production" { 80 | action "promote-production" { 81 | organization = "hashicorp" 82 | repository = "crt-workflows-common" 83 | workflow = "promote-production" 84 | depends = null 85 | config = "" 86 | } 87 | 88 | depends = ["trigger-production"] 89 | 90 | notification { 91 | on = "always" 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /.release/release-metadata.hcl: -------------------------------------------------------------------------------- 1 | url_source_repository = "https://github.com/hashicorp/terraform-provider-cloudinit" 2 | url_project_website = "https://registry.terraform.io/providers/hashicorp/cloudinit" 3 | url_license = "https://github.com/hashicorp/terraform-provider-cloudinit/blob/main/LICENSE" 4 | url_release_notes = "https://github.com/hashicorp/terraform-provider-cloudinit/blob/main/CHANGELOG.md" 5 | -------------------------------------------------------------------------------- /.release/security-scan.hcl: -------------------------------------------------------------------------------- 1 | # Reference: https://github.com/hashicorp/security-scanner/blob/main/CONFIG.md#binary (private repository) 2 | 3 | binary { 4 | secrets { 5 | all = true 6 | } 7 | go_modules = true 8 | osv = true 9 | oss_index = false 10 | nvd = false 11 | } 12 | -------------------------------------------------------------------------------- /.release/terraform-provider-cloudinit-artifacts.hcl: -------------------------------------------------------------------------------- 1 | schema = 1 2 | artifacts { 3 | zip = [ 4 | "terraform-provider-cloudinit_${version}_darwin_amd64.zip", 5 | "terraform-provider-cloudinit_${version}_darwin_arm64.zip", 6 | "terraform-provider-cloudinit_${version}_freebsd_386.zip", 7 | "terraform-provider-cloudinit_${version}_freebsd_amd64.zip", 8 | "terraform-provider-cloudinit_${version}_freebsd_arm.zip", 9 | "terraform-provider-cloudinit_${version}_linux_386.zip", 10 | "terraform-provider-cloudinit_${version}_linux_amd64.zip", 11 | "terraform-provider-cloudinit_${version}_linux_arm.zip", 12 | "terraform-provider-cloudinit_${version}_linux_arm64.zip", 13 | "terraform-provider-cloudinit_${version}_windows_386.zip", 14 | "terraform-provider-cloudinit_${version}_windows_amd64.zip", 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.3.7 (April 21, 2025) 2 | 3 | NOTES: 4 | 5 | * Update dependencies ([#339](https://github.com/hashicorp/terraform-provider-cloudinit/issues/339)) 6 | 7 | ## 2.3.7-alpha1 (March 04, 2025) 8 | 9 | NOTES: 10 | 11 | * all: This release is being used to test new build and release actions. 12 | 13 | ## 2.3.6 (February 27, 2025) 14 | 15 | NOTES: 16 | 17 | * all: This release contains no functionality changes. It is being used to fix release metadata in the registry from the previous alpha1 release. ([#318](https://github.com/hashicorp/terraform-provider-cloudinit/issues/318)) 18 | 19 | ## 2.3.6-alpha1 (December 05, 2024) 20 | 21 | NOTES: 22 | 23 | * all: This release contains no functionality changes. It is released using new build and release Actions. ([#293](https://github.com/hashicorp/terraform-provider-cloudinit/issues/293)) 24 | 25 | ## 2.3.5 (September 10, 2024) 26 | 27 | NOTES: 28 | 29 | * all: This release introduces no functional changes. It does however include dependency updates which address upstream CVEs. ([#263](https://github.com/hashicorp/terraform-provider-cloudinit/issues/263)) 30 | 31 | ## 2.3.4 (April 22, 2024) 32 | 33 | NOTES: 34 | 35 | * all: This release contains no functionality changes, only the inclusion of the LICENSE file in the release archives ([#228](https://github.com/hashicorp/terraform-provider-cloudinit/issues/228)) 36 | 37 | ## 2.3.3 (November 29, 2023) 38 | 39 | NOTES: 40 | 41 | * This release introduces no functional changes. It does however include dependency updates which address upstream CVEs. ([#186](https://github.com/hashicorp/terraform-provider-cloudinit/issues/186)) 42 | 43 | ## 2.3.2 (February 23, 2023) 44 | 45 | BUG FIXES: 46 | 47 | * cloudinit_config: Remove length validation to allow empty content string in part blocks ([#105](https://github.com/hashicorp/terraform-provider-cloudinit/issues/105)) 48 | 49 | ## 2.3.1 (February 22, 2023) 50 | 51 | BUG FIXES: 52 | 53 | * cloudinit_config: Fixed handling of unknown values in `part` blocks ([#103](https://github.com/hashicorp/terraform-provider-cloudinit/issues/103)) 54 | 55 | ## 2.3.0 (February 22, 2023) 56 | 57 | NOTES: 58 | 59 | * provider: Rewritten to use the [`terraform-plugin-framework`](https://www.terraform.io/plugin/framework) ([#96](https://github.com/hashicorp/terraform-provider-cloudinit/issues/96)) 60 | 61 | ## 2.2.0 (February 19, 2021) 62 | 63 | Binary releases of this provider will now include the darwin-arm64 platform. This version contains no further changes. 64 | 65 | ## 2.1.0 (November 26, 2020) 66 | 67 | NEW FEATURES: 68 | 69 | * MIMEBOUNDARY can now be customised with `boundary` ([#7](https://github.com/hashicorp/terraform-provider-cloudinit/issues/7)). 70 | 71 | ## 2.0.0 (October 12, 2020) 72 | 73 | Binary releases of this provider will now include the linux-arm64 platform. 74 | 75 | BREAKING CHANGES: 76 | 77 | * Upgrade to version 2 of the Terraform Plugin SDK, which drops support for Terraform 0.11. This provider will continue to work as expected for users of Terraform 0.11, which will not download the new version. ([#3](https://github.com/hashicorp/terraform-provider-cloudinit/issues/3)) 78 | 79 | ## 1.0.0 (April 14, 2020) 80 | 81 | Initial release. This provider exposes one resource, cloudinit_config, which is identical to the template_cloudinit_config resource in terraform-provider-template. 82 | -------------------------------------------------------------------------------- /DESIGN.md: -------------------------------------------------------------------------------- 1 | # cloud-init Provider Design 2 | 3 | Cloud-init is a commonly-used startup configuration utility for cloud compute instances. The cloud-init provider offers functionality to render a MIME multi-part file for use with cloud-init. Using a MIME multi-part file, the user can specify more than one type of user data for cloud-init to consume. If you only have one type of user data, you can leverage the built-in [`templatefile`](https://www.terraform.io/docs/configuration/functions/templatefile.html) function and a static file (like `.yml`). 4 | 5 | Below we have a collection of _Goals_ and _Patterns_: they represent the guiding principles applied during the 6 | development of this provider. Some are in place, others are ongoing processes, others are still just inspirational. 7 | 8 | ## Goals 9 | 10 | * [_Stability over features_](.github/CONTRIBUTING.md) 11 | * Provide a managed resource and data source to generate a cloud-init MIME multi-part file 12 | * This multi-part file can include multiple user data formats, such as **[Cloud config](https://cloudinit.readthedocs.io/en/latest/explanation/format.html#cloud-config-data)** or **[Shell scripts](https://cloudinit.readthedocs.io/en/latest/explanation/format.html#user-data-script)** 13 | * See the official [cloud-init](https://cloudinit.readthedocs.io/en/latest/explanation/index.html) documentation for more information on user data formats and cloud provider specific documentation 14 | 15 | ## Patterns 16 | 17 | Specific to this provider: 18 | 19 | * The managed resource and data source use the same underlying code to generate the MIME multi-part file. 20 | 21 | General to development: 22 | 23 | * **Avoid repetition**: the entities managed can sometimes require similar pieces of logic and/or schema to be realised. 24 | When this happens it's important to keep the code shared in communal sections, so to avoid having to modify code in 25 | multiple places when they start changing. 26 | * **Test expectations as well as bugs**: While it's typical to write tests to exercise a new functionality, it's key to 27 | also provide tests for issues that get identified and fixed, so to prove resolution as well as avoid regression. 28 | * **Automate boring tasks**: Processes that are manual, repetitive and can be automated, should be. In addition to be a 29 | time-saving practice, this ensures consistency and reduces human error (ex. static code analysis). 30 | * **Semantic versioning**: Adhering to HashiCorp's own 31 | [Versioning Specification](https://www.terraform.io/plugin/sdkv2/best-practices/versioning#versioning-specification) 32 | ensures we provide a consistent practitioner experience, and a clear process to deprecation and decommission. -------------------------------------------------------------------------------- /GNUmakefile: -------------------------------------------------------------------------------- 1 | default: build 2 | 3 | build: 4 | go build -v ./... 5 | 6 | install: build 7 | go install -v ./... 8 | 9 | # See https://golangci-lint.run/ 10 | lint: 11 | golangci-lint run 12 | 13 | # Generate docs and copywrite headers 14 | generate: 15 | cd tools; go generate ./... 16 | 17 | fmt: 18 | gofmt -s -w -e . 19 | 20 | test: 21 | go test -v -cover -timeout=120s -parallel=4 ./... 22 | 23 | testacc: 24 | TF_ACC=1 go test -v -cover -timeout 120m ./... 25 | 26 | .PHONY: build install lint generate fmt test testacc -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 HashiCorp, Inc. 2 | 3 | Mozilla Public License Version 2.0 4 | ================================== 5 | 6 | 1. Definitions 7 | -------------- 8 | 9 | 1.1. "Contributor" 10 | means each individual or legal entity that creates, contributes to 11 | the creation of, or owns Covered Software. 12 | 13 | 1.2. "Contributor Version" 14 | means the combination of the Contributions of others (if any) used 15 | by a Contributor and that particular Contributor's Contribution. 16 | 17 | 1.3. "Contribution" 18 | means Covered Software of a particular Contributor. 19 | 20 | 1.4. "Covered Software" 21 | means Source Code Form to which the initial Contributor has attached 22 | the notice in Exhibit A, the Executable Form of such Source Code 23 | Form, and Modifications of such Source Code Form, in each case 24 | including portions thereof. 25 | 26 | 1.5. "Incompatible With Secondary Licenses" 27 | means 28 | 29 | (a) that the initial Contributor has attached the notice described 30 | in Exhibit B to the Covered Software; or 31 | 32 | (b) that the Covered Software was made available under the terms of 33 | version 1.1 or earlier of the License, but not also under the 34 | terms of a Secondary License. 35 | 36 | 1.6. "Executable Form" 37 | means any form of the work other than Source Code Form. 38 | 39 | 1.7. "Larger Work" 40 | means a work that combines Covered Software with other material, in 41 | a separate file or files, that is not Covered Software. 42 | 43 | 1.8. "License" 44 | means this document. 45 | 46 | 1.9. "Licensable" 47 | means having the right to grant, to the maximum extent possible, 48 | whether at the time of the initial grant or subsequently, any and 49 | all of the rights conveyed by this License. 50 | 51 | 1.10. "Modifications" 52 | means any of the following: 53 | 54 | (a) any file in Source Code Form that results from an addition to, 55 | deletion from, or modification of the contents of Covered 56 | Software; or 57 | 58 | (b) any new file in Source Code Form that contains any Covered 59 | Software. 60 | 61 | 1.11. "Patent Claims" of a Contributor 62 | means any patent claim(s), including without limitation, method, 63 | process, and apparatus claims, in any patent Licensable by such 64 | Contributor that would be infringed, but for the grant of the 65 | License, by the making, using, selling, offering for sale, having 66 | made, import, or transfer of either its Contributions or its 67 | Contributor Version. 68 | 69 | 1.12. "Secondary License" 70 | means either the GNU General Public License, Version 2.0, the GNU 71 | Lesser General Public License, Version 2.1, the GNU Affero General 72 | Public License, Version 3.0, or any later versions of those 73 | licenses. 74 | 75 | 1.13. "Source Code Form" 76 | means the form of the work preferred for making modifications. 77 | 78 | 1.14. "You" (or "Your") 79 | means an individual or a legal entity exercising rights under this 80 | License. For legal entities, "You" includes any entity that 81 | controls, is controlled by, or is under common control with You. For 82 | purposes of this definition, "control" means (a) the power, direct 83 | or indirect, to cause the direction or management of such entity, 84 | whether by contract or otherwise, or (b) ownership of more than 85 | fifty percent (50%) of the outstanding shares or beneficial 86 | ownership of such entity. 87 | 88 | 2. License Grants and Conditions 89 | -------------------------------- 90 | 91 | 2.1. Grants 92 | 93 | Each Contributor hereby grants You a world-wide, royalty-free, 94 | non-exclusive license: 95 | 96 | (a) under intellectual property rights (other than patent or trademark) 97 | Licensable by such Contributor to use, reproduce, make available, 98 | modify, display, perform, distribute, and otherwise exploit its 99 | Contributions, either on an unmodified basis, with Modifications, or 100 | as part of a Larger Work; and 101 | 102 | (b) under Patent Claims of such Contributor to make, use, sell, offer 103 | for sale, have made, import, and otherwise transfer either its 104 | Contributions or its Contributor Version. 105 | 106 | 2.2. Effective Date 107 | 108 | The licenses granted in Section 2.1 with respect to any Contribution 109 | become effective for each Contribution on the date the Contributor first 110 | distributes such Contribution. 111 | 112 | 2.3. Limitations on Grant Scope 113 | 114 | The licenses granted in this Section 2 are the only rights granted under 115 | this License. No additional rights or licenses will be implied from the 116 | distribution or licensing of Covered Software under this License. 117 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 118 | Contributor: 119 | 120 | (a) for any code that a Contributor has removed from Covered Software; 121 | or 122 | 123 | (b) for infringements caused by: (i) Your and any other third party's 124 | modifications of Covered Software, or (ii) the combination of its 125 | Contributions with other software (except as part of its Contributor 126 | Version); or 127 | 128 | (c) under Patent Claims infringed by Covered Software in the absence of 129 | its Contributions. 130 | 131 | This License does not grant any rights in the trademarks, service marks, 132 | or logos of any Contributor (except as may be necessary to comply with 133 | the notice requirements in Section 3.4). 134 | 135 | 2.4. Subsequent Licenses 136 | 137 | No Contributor makes additional grants as a result of Your choice to 138 | distribute the Covered Software under a subsequent version of this 139 | License (see Section 10.2) or under the terms of a Secondary License (if 140 | permitted under the terms of Section 3.3). 141 | 142 | 2.5. Representation 143 | 144 | Each Contributor represents that the Contributor believes its 145 | Contributions are its original creation(s) or it has sufficient rights 146 | to grant the rights to its Contributions conveyed by this License. 147 | 148 | 2.6. Fair Use 149 | 150 | This License is not intended to limit any rights You have under 151 | applicable copyright doctrines of fair use, fair dealing, or other 152 | equivalents. 153 | 154 | 2.7. Conditions 155 | 156 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 157 | in Section 2.1. 158 | 159 | 3. Responsibilities 160 | ------------------- 161 | 162 | 3.1. Distribution of Source Form 163 | 164 | All distribution of Covered Software in Source Code Form, including any 165 | Modifications that You create or to which You contribute, must be under 166 | the terms of this License. You must inform recipients that the Source 167 | Code Form of the Covered Software is governed by the terms of this 168 | License, and how they can obtain a copy of this License. You may not 169 | attempt to alter or restrict the recipients' rights in the Source Code 170 | Form. 171 | 172 | 3.2. Distribution of Executable Form 173 | 174 | If You distribute Covered Software in Executable Form then: 175 | 176 | (a) such Covered Software must also be made available in Source Code 177 | Form, as described in Section 3.1, and You must inform recipients of 178 | the Executable Form how they can obtain a copy of such Source Code 179 | Form by reasonable means in a timely manner, at a charge no more 180 | than the cost of distribution to the recipient; and 181 | 182 | (b) You may distribute such Executable Form under the terms of this 183 | License, or sublicense it under different terms, provided that the 184 | license for the Executable Form does not attempt to limit or alter 185 | the recipients' rights in the Source Code Form under this License. 186 | 187 | 3.3. Distribution of a Larger Work 188 | 189 | You may create and distribute a Larger Work under terms of Your choice, 190 | provided that You also comply with the requirements of this License for 191 | the Covered Software. If the Larger Work is a combination of Covered 192 | Software with a work governed by one or more Secondary Licenses, and the 193 | Covered Software is not Incompatible With Secondary Licenses, this 194 | License permits You to additionally distribute such Covered Software 195 | under the terms of such Secondary License(s), so that the recipient of 196 | the Larger Work may, at their option, further distribute the Covered 197 | Software under the terms of either this License or such Secondary 198 | License(s). 199 | 200 | 3.4. Notices 201 | 202 | You may not remove or alter the substance of any license notices 203 | (including copyright notices, patent notices, disclaimers of warranty, 204 | or limitations of liability) contained within the Source Code Form of 205 | the Covered Software, except that You may alter any license notices to 206 | the extent required to remedy known factual inaccuracies. 207 | 208 | 3.5. Application of Additional Terms 209 | 210 | You may choose to offer, and to charge a fee for, warranty, support, 211 | indemnity or liability obligations to one or more recipients of Covered 212 | Software. However, You may do so only on Your own behalf, and not on 213 | behalf of any Contributor. You must make it absolutely clear that any 214 | such warranty, support, indemnity, or liability obligation is offered by 215 | You alone, and You hereby agree to indemnify every Contributor for any 216 | liability incurred by such Contributor as a result of warranty, support, 217 | indemnity or liability terms You offer. You may include additional 218 | disclaimers of warranty and limitations of liability specific to any 219 | jurisdiction. 220 | 221 | 4. Inability to Comply Due to Statute or Regulation 222 | --------------------------------------------------- 223 | 224 | If it is impossible for You to comply with any of the terms of this 225 | License with respect to some or all of the Covered Software due to 226 | statute, judicial order, or regulation then You must: (a) comply with 227 | the terms of this License to the maximum extent possible; and (b) 228 | describe the limitations and the code they affect. Such description must 229 | be placed in a text file included with all distributions of the Covered 230 | Software under this License. Except to the extent prohibited by statute 231 | or regulation, such description must be sufficiently detailed for a 232 | recipient of ordinary skill to be able to understand it. 233 | 234 | 5. Termination 235 | -------------- 236 | 237 | 5.1. The rights granted under this License will terminate automatically 238 | if You fail to comply with any of its terms. However, if You become 239 | compliant, then the rights granted under this License from a particular 240 | Contributor are reinstated (a) provisionally, unless and until such 241 | Contributor explicitly and finally terminates Your grants, and (b) on an 242 | ongoing basis, if such Contributor fails to notify You of the 243 | non-compliance by some reasonable means prior to 60 days after You have 244 | come back into compliance. Moreover, Your grants from a particular 245 | Contributor are reinstated on an ongoing basis if such Contributor 246 | notifies You of the non-compliance by some reasonable means, this is the 247 | first time You have received notice of non-compliance with this License 248 | from such Contributor, and You become compliant prior to 30 days after 249 | Your receipt of the notice. 250 | 251 | 5.2. If You initiate litigation against any entity by asserting a patent 252 | infringement claim (excluding declaratory judgment actions, 253 | counter-claims, and cross-claims) alleging that a Contributor Version 254 | directly or indirectly infringes any patent, then the rights granted to 255 | You by any and all Contributors for the Covered Software under Section 256 | 2.1 of this License shall terminate. 257 | 258 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 259 | end user license agreements (excluding distributors and resellers) which 260 | have been validly granted by You or Your distributors under this License 261 | prior to termination shall survive termination. 262 | 263 | ************************************************************************ 264 | * * 265 | * 6. Disclaimer of Warranty * 266 | * ------------------------- * 267 | * * 268 | * Covered Software is provided under this License on an "as is" * 269 | * basis, without warranty of any kind, either expressed, implied, or * 270 | * statutory, including, without limitation, warranties that the * 271 | * Covered Software is free of defects, merchantable, fit for a * 272 | * particular purpose or non-infringing. The entire risk as to the * 273 | * quality and performance of the Covered Software is with You. * 274 | * Should any Covered Software prove defective in any respect, You * 275 | * (not any Contributor) assume the cost of any necessary servicing, * 276 | * repair, or correction. This disclaimer of warranty constitutes an * 277 | * essential part of this License. No use of any Covered Software is * 278 | * authorized under this License except under this disclaimer. * 279 | * * 280 | ************************************************************************ 281 | 282 | ************************************************************************ 283 | * * 284 | * 7. Limitation of Liability * 285 | * -------------------------- * 286 | * * 287 | * Under no circumstances and under no legal theory, whether tort * 288 | * (including negligence), contract, or otherwise, shall any * 289 | * Contributor, or anyone who distributes Covered Software as * 290 | * permitted above, be liable to You for any direct, indirect, * 291 | * special, incidental, or consequential damages of any character * 292 | * including, without limitation, damages for lost profits, loss of * 293 | * goodwill, work stoppage, computer failure or malfunction, or any * 294 | * and all other commercial damages or losses, even if such party * 295 | * shall have been informed of the possibility of such damages. This * 296 | * limitation of liability shall not apply to liability for death or * 297 | * personal injury resulting from such party's negligence to the * 298 | * extent applicable law prohibits such limitation. Some * 299 | * jurisdictions do not allow the exclusion or limitation of * 300 | * incidental or consequential damages, so this exclusion and * 301 | * limitation may not apply to You. * 302 | * * 303 | ************************************************************************ 304 | 305 | 8. Litigation 306 | ------------- 307 | 308 | Any litigation relating to this License may be brought only in the 309 | courts of a jurisdiction where the defendant maintains its principal 310 | place of business and such litigation shall be governed by laws of that 311 | jurisdiction, without reference to its conflict-of-law provisions. 312 | Nothing in this Section shall prevent a party's ability to bring 313 | cross-claims or counter-claims. 314 | 315 | 9. Miscellaneous 316 | ---------------- 317 | 318 | This License represents the complete agreement concerning the subject 319 | matter hereof. If any provision of this License is held to be 320 | unenforceable, such provision shall be reformed only to the extent 321 | necessary to make it enforceable. Any law or regulation which provides 322 | that the language of a contract shall be construed against the drafter 323 | shall not be used to construe this License against a Contributor. 324 | 325 | 10. Versions of the License 326 | --------------------------- 327 | 328 | 10.1. New Versions 329 | 330 | Mozilla Foundation is the license steward. Except as provided in Section 331 | 10.3, no one other than the license steward has the right to modify or 332 | publish new versions of this License. Each version will be given a 333 | distinguishing version number. 334 | 335 | 10.2. Effect of New Versions 336 | 337 | You may distribute the Covered Software under the terms of the version 338 | of the License under which You originally received the Covered Software, 339 | or under the terms of any subsequent version published by the license 340 | steward. 341 | 342 | 10.3. Modified Versions 343 | 344 | If you create software not governed by this License, and you want to 345 | create a new license for such software, you may create and use a 346 | modified version of this License if you rename the license and remove 347 | any references to the name of the license steward (except to note that 348 | such modified license differs from this License). 349 | 350 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 351 | Licenses 352 | 353 | If You choose to distribute Source Code Form that is Incompatible With 354 | Secondary Licenses under the terms of this version of the License, the 355 | notice described in Exhibit B of this License must be attached. 356 | 357 | Exhibit A - Source Code Form License Notice 358 | ------------------------------------------- 359 | 360 | This Source Code Form is subject to the terms of the Mozilla Public 361 | License, v. 2.0. If a copy of the MPL was not distributed with this 362 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 363 | 364 | If it is not possible or desirable to put the notice in a particular 365 | file, then You may include the notice in a location (such as a LICENSE 366 | file in a relevant directory) where a recipient would be likely to look 367 | for such a notice. 368 | 369 | You may add additional accurate notices of copyright ownership. 370 | 371 | Exhibit B - "Incompatible With Secondary Licenses" Notice 372 | --------------------------------------------------------- 373 | 374 | This Source Code Form is "Incompatible With Secondary Licenses", as 375 | defined by the Mozilla Public License, v. 2.0. 376 | -------------------------------------------------------------------------------- /META.d/_summary.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | schema: 1.1 3 | 4 | partition: tf-ecosystem 5 | 6 | summary: 7 | owner: team-tf-core-plugins 8 | description: | 9 | Utility provider that exposes the cloudinit_config data source which renders a multipart MIME configuration for use with cloud-init (previously available as the template_cloudinit_config resource in the template provider) 10 | visibility: public 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Terraform Provider: cloud-init 2 | 3 | The cloud-init provider supports rendering [cloud-init](https://cloudinit.readthedocs.io) configurations in a [MIME multi-part file](https://cloudinit.readthedocs.io/en/latest/explanation/format.html#mime-multi-part-archive). 4 | 5 | > _This provider is intended to replace the [template provider](https://www.terraform.io/docs/providers/template/). General templating can now be achieved through the [`templatefile`](https://www.terraform.io/docs/configuration/functions/templatefile.html) function, without creating a separate data resource._ 6 | > _This provider exposes the `cloudinit_config` data source and resource, previously available as the [`template_cloudinit_config`](https://www.terraform.io/docs/providers/template/d/cloudinit_config.html) in the template provider._ 7 | 8 | ## Documentation, questions and discussions 9 | 10 | Official documentation on how to use this provider can be found on the 11 | [Terraform Registry](https://registry.terraform.io/providers/hashicorp/cloudinit/latest/docs). 12 | In case of specific questions or discussions, please use the 13 | HashiCorp [Terraform Providers Discuss forums](https://discuss.hashicorp.com/c/terraform-providers/31), 14 | in accordance with HashiCorp [Community Guidelines](https://www.hashicorp.com/community-guidelines). 15 | 16 | We also provide: 17 | 18 | * [Support](.github/SUPPORT.md) page for help when using the provider 19 | * [Contributing](.github/CONTRIBUTING.md) guidelines in case you want to help this project 20 | * [Design](DESIGN.md) documentation to understand the scope and maintenance decisions 21 | 22 | The remainder of this document will focus on the development aspects of the provider. 23 | 24 | 25 | ## Requirements 26 | 27 | * [Terraform](https://www.terraform.io/downloads) 28 | * [Go](https://go.dev/doc/install) (1.23) 29 | * [GNU Make](https://www.gnu.org/software/make/) 30 | * [golangci-lint](https://golangci-lint.run/usage/install/#local-installation) (optional) 31 | 32 | ## Development 33 | 34 | ### Building 35 | 36 | 1. `git clone` this repository and `cd` into its directory 37 | 2. `make` will trigger the Golang build 38 | 39 | The provided `GNUmakefile` defines additional commands generally useful during development, 40 | like for running tests, generating documentation, code formatting and linting. 41 | Taking a look at it's content is recommended. 42 | 43 | ### Testing 44 | 45 | In order to test the provider, you can run 46 | 47 | * `make test` to run provider tests 48 | * `make testacc` to run provider acceptance tests 49 | 50 | It's important to note that acceptance tests (`testacc`) will actually spawn 51 | `terraform` and the provider. Read more about they work on the 52 | [official page](https://www.terraform.io/plugin/sdkv2/testing/acceptance-tests). 53 | 54 | ### Generating documentation 55 | 56 | This provider uses [terraform-plugin-docs](https://github.com/hashicorp/terraform-plugin-docs/) 57 | to generate documentation and store it in the `docs/` directory. 58 | Once a release is cut, the Terraform Registry will download the documentation from `docs/` 59 | and associate it with the release version. Read more about how this works on the 60 | [official page](https://www.terraform.io/registry/providers/docs). 61 | 62 | Use `make generate` to ensure the documentation is regenerated with any changes. 63 | 64 | ### Using a development build 65 | 66 | If [running tests and acceptance tests](#testing) isn't enough, it's possible to set up a local terraform configuration 67 | to use a development builds of the provider. This can be achieved by leveraging the Terraform CLI 68 | [configuration file development overrides](https://www.terraform.io/cli/config/config-file#development-overrides-for-provider-developers). 69 | 70 | First, use `make install` to place a fresh development build of the provider in your 71 | [`${GOBIN}`](https://pkg.go.dev/cmd/go#hdr-Compile_and_install_packages_and_dependencies) 72 | (defaults to `${GOPATH}/bin` or `${HOME}/go/bin` if `${GOPATH}` is not set). Repeat 73 | this every time you make changes to the provider locally. 74 | 75 | Then, setup your environment following [these instructions](https://www.terraform.io/plugin/debugging#terraform-cli-development-overrides) 76 | to make your local terraform use your local build. 77 | 78 | ### Testing GitHub Actions 79 | 80 | This project uses [GitHub Actions](https://docs.github.com/en/actions/automating-builds-and-tests) to realize its CI. 81 | 82 | Sometimes it might be helpful to locally reproduce the behaviour of those actions, 83 | and for this we use [act](https://github.com/nektos/act). Once installed, you can _simulate_ the actions executed 84 | when opening a PR with: 85 | 86 | ```shell 87 | # List of workflows for the 'pull_request' action 88 | $ act -l pull_request 89 | 90 | # Execute the workflows associated with the `pull_request' action 91 | $ act pull_request 92 | ``` 93 | 94 | ## Releasing 95 | 96 | The releasable builds are generated from the [build GH workflow](./.github/workflows/build.yml) and the release/promotion process 97 | is completed via internal HashiCorp deployment tooling. Prior to release, the changelog should be updated in `main` with 98 | the changie tool, example: 99 | 100 | ```sh 101 | changie batch 2.3.7 && changie merge 102 | ``` 103 | 104 | ## License 105 | 106 | [Mozilla Public License v2.0](./LICENSE) 107 | -------------------------------------------------------------------------------- /docs/cdktf/python/data-sources/config.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "cloudinit_config Data Source - terraform-provider-cloudinit" 3 | description: |- 4 | Renders a multi-part MIME configuration https://cloudinit.readthedocs.io/en/latest/explanation/format.html#mime-multi-part-archive for use with cloud-init https://cloudinit.readthedocs.io/en/latest/. 5 | Cloud-init is a commonly-used startup configuration utility for cloud compute instances. It accepts configuration via provider-specific user data mechanisms, such as user_data for Amazon EC2 instances. Multi-part MIME is one of the data formats it accepts. For more information, see User-Data Formats https://cloudinit.readthedocs.io/en/latest/explanation/format.html in the cloud-init manual. 6 | This is not a generalized utility for producing multi-part MIME messages. Its feature set is specialized for cloud-init multi-part MIME messages. 7 | --- 8 | 9 | 10 | 11 | # cloudinit_config (Data Source) 12 | 13 | Renders a [multi-part MIME configuration](https://cloudinit.readthedocs.io/en/latest/explanation/format.html#mime-multi-part-archive) for use with [cloud-init](https://cloudinit.readthedocs.io/en/latest/). 14 | 15 | Cloud-init is a commonly-used startup configuration utility for cloud compute instances. It accepts configuration via provider-specific user data mechanisms, such as `user_data` for Amazon EC2 instances. Multi-part MIME is one of the data formats it accepts. For more information, see [User-Data Formats](https://cloudinit.readthedocs.io/en/latest/explanation/format.html) in the cloud-init manual. 16 | 17 | This is not a generalized utility for producing multi-part MIME messages. Its feature set is specialized for cloud-init multi-part MIME messages. 18 | 19 | ## Example Usage 20 | 21 | ### Config 22 | ```python 23 | # DO NOT EDIT. Code generated by 'cdktf convert' - Please report bugs at https://cdk.tf/bug 24 | from constructs import Construct 25 | from cdktf import Fn, Token, TerraformStack 26 | # 27 | # Provider bindings are generated by running `cdktf get`. 28 | # See https://cdk.tf/provider-generation for more details. 29 | # 30 | from imports.cloudinit.data_cloudinit_config import DataCloudinitConfig 31 | class MyConvertedCode(TerraformStack): 32 | def __init__(self, scope, name): 33 | super().__init__(scope, name) 34 | DataCloudinitConfig(self, "foobar", 35 | base64_encode=False, 36 | gzip=False, 37 | part=[DataCloudinitConfigPart( 38 | content=Token.as_string(Fn.file("${path.module}/hello-script.sh")), 39 | content_type="text/x-shellscript", 40 | filename="hello-script.sh" 41 | ), DataCloudinitConfigPart( 42 | content=Token.as_string(Fn.file("${path.module}/cloud-config.yaml")), 43 | content_type="text/cloud-config", 44 | filename="cloud-config.yaml" 45 | ) 46 | ] 47 | ) 48 | ``` 49 | 50 | ### hello-script.sh 51 | ```shell 52 | #!/bin/sh 53 | echo "Hello World! I'm starting up now at $(date -R)!" 54 | ``` 55 | 56 | ### cloud-config.yaml 57 | ```yaml 58 | #cloud-config 59 | # See documentation for more configuration examples 60 | # https://cloudinit.readthedocs.io/en/latest/reference/examples.html 61 | 62 | # Install arbitrary packages 63 | # https://cloudinit.readthedocs.io/en/latest/reference/examples.html#install-arbitrary-packages 64 | packages: 65 | - python 66 | # Run commands on first boot 67 | # https://cloudinit.readthedocs.io/en/latest/reference/examples.html#run-commands-on-first-boot 68 | runcmd: 69 | - [ ls, -l, / ] 70 | - [ sh, -xc, "echo $(date) ': hello world!'" ] 71 | - [ sh, -c, echo "=========hello world=========" ] 72 | - ls -l /root 73 | ``` 74 | 75 | 76 | 77 | ## Schema 78 | 79 | ### Required 80 | 81 | - `part` (Block List) A nested block type which adds a file to the generated cloud-init configuration. Use multiple `part` blocks to specify multiple files, which will be included in order of declaration in the final MIME document. (see [below for nested schema](#nestedblock--part)) 82 | 83 | ### Optional 84 | 85 | - `base64_encode` (Boolean) Specify whether or not to base64 encode the `rendered` output. Defaults to `true`, and cannot be disabled if gzip is `true`. 86 | - `boundary` (String) Specify the Writer's default boundary separator. Defaults to `MIMEBOUNDARY`. 87 | - `gzip` (Boolean) Specify whether or not to gzip the `rendered` output. Defaults to `true`. 88 | 89 | ### Read-Only 90 | 91 | - `id` (String) [CRC-32](https://pkg.go.dev/hash/crc32) checksum of `rendered` cloud-init config. 92 | - `rendered` (String) The final rendered multi-part cloud-init config. 93 | 94 | 95 | ### Nested Schema for `part` 96 | 97 | Required: 98 | 99 | - `content` (String) Body content for the part. 100 | 101 | Optional: 102 | 103 | - `content_type` (String) A MIME-style content type to report in the header for the part. Defaults to `text/plain` 104 | - `filename` (String) A filename to report in the header for the part. 105 | - `merge_type` (String) A value for the `X-Merge-Type` header of the part, to control [cloud-init merging behavior](https://cloudinit.readthedocs.io/en/latest/reference/merging.html). 106 | 107 | -------------------------------------------------------------------------------- /docs/cdktf/python/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "cloudinit Provider" 3 | description: |- 4 | The cloud-init Terraform provider exposes the cloudinit_config data source, previously available as the template_cloudinit_config resource in the template provider https://registry.terraform.io/providers/hashicorp/template/latest/docs/data-sources/cloudinit_config, which renders a multipart MIME configuration https://cloudinit.readthedocs.io/en/latest/explanation/format.html#mime-multi-part-archive for use with cloud-init https://cloudinit.readthedocs.io/en/latest/. 5 | --- 6 | 7 | 8 | 9 | # cloudinit Provider 10 | 11 | The cloud-init Terraform provider exposes the `cloudinit_config` data source, previously available as the `template_cloudinit_config` resource [in the template provider](https://registry.terraform.io/providers/hashicorp/template/latest/docs/data-sources/cloudinit_config), which renders a [multipart MIME configuration](https://cloudinit.readthedocs.io/en/latest/explanation/format.html#mime-multi-part-archive) for use with [cloud-init](https://cloudinit.readthedocs.io/en/latest/). 12 | 13 | This provider requires no configuration. For information on the resources it provides, see the navigation bar. 14 | -------------------------------------------------------------------------------- /docs/cdktf/python/resources/config.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "cloudinit_config Resource - terraform-provider-cloudinit" 3 | description: |- 4 | Renders a multi-part MIME configuration https://cloudinit.readthedocs.io/en/latest/explanation/format.html#mime-multi-part-archive for use with cloud-init https://cloudinit.readthedocs.io/en/latest/. 5 | Cloud-init is a commonly-used startup configuration utility for cloud compute instances. It accepts configuration via provider-specific user data mechanisms, such as user_data for Amazon EC2 instances. Multi-part MIME is one of the data formats it accepts. For more information, see User-Data Formats https://cloudinit.readthedocs.io/en/latest/explanation/format.html in the cloud-init manual. 6 | This is not a generalized utility for producing multi-part MIME messages. Its feature set is specialized for cloud-init multi-part MIME messages. 7 | --- 8 | 9 | 10 | 11 | # cloudinit_config (Resource) 12 | 13 | ~> **This resource is deprecated** Please use the [cloudinit_config](https://registry.terraform.io/providers/hashicorp/cloudinit/latest/docs/data-sources/config) 14 | data source instead. 15 | 16 | Renders a [multi-part MIME configuration](https://cloudinit.readthedocs.io/en/latest/explanation/format.html#mime-multi-part-archive) for use with [cloud-init](https://cloudinit.readthedocs.io/en/latest/). 17 | 18 | Cloud-init is a commonly-used startup configuration utility for cloud compute instances. It accepts configuration via provider-specific user data mechanisms, such as `user_data` for Amazon EC2 instances. Multi-part MIME is one of the data formats it accepts. For more information, see [User-Data Formats](https://cloudinit.readthedocs.io/en/latest/explanation/format.html) in the cloud-init manual. 19 | 20 | This is not a generalized utility for producing multi-part MIME messages. Its feature set is specialized for cloud-init multi-part MIME messages. 21 | 22 | ## Example Usage 23 | 24 | ### Config 25 | ```python 26 | # DO NOT EDIT. Code generated by 'cdktf convert' - Please report bugs at https://cdk.tf/bug 27 | from constructs import Construct 28 | from cdktf import Fn, Token, TerraformStack 29 | # 30 | # Provider bindings are generated by running `cdktf get`. 31 | # See https://cdk.tf/provider-generation for more details. 32 | # 33 | from imports.cloudinit.config import Config 34 | class MyConvertedCode(TerraformStack): 35 | def __init__(self, scope, name): 36 | super().__init__(scope, name) 37 | Config(self, "foobar", 38 | base64_encode=False, 39 | gzip=False, 40 | part=[ConfigPart( 41 | content=Token.as_string(Fn.file("${path.module}/hello-script.sh")), 42 | content_type="text/x-shellscript", 43 | filename="hello-script.sh" 44 | ), ConfigPart( 45 | content=Token.as_string(Fn.file("${path.module}/cloud-config.yaml")), 46 | content_type="text/cloud-config", 47 | filename="cloud-config.yaml" 48 | ) 49 | ] 50 | ) 51 | ``` 52 | 53 | ### hello-script.sh 54 | ```shell 55 | #!/bin/sh 56 | echo "Hello World! I'm starting up now at $(date -R)!" 57 | ``` 58 | 59 | ### cloud-config.yaml 60 | ```yaml 61 | #cloud-config 62 | # See documentation for more configuration examples 63 | # https://cloudinit.readthedocs.io/en/latest/reference/examples.html 64 | 65 | # Install arbitrary packages 66 | # https://cloudinit.readthedocs.io/en/latest/reference/examples.html#install-arbitrary-packages 67 | packages: 68 | - python 69 | # Run commands on first boot 70 | # https://cloudinit.readthedocs.io/en/latest/reference/examples.html#run-commands-on-first-boot 71 | runcmd: 72 | - [ ls, -l, / ] 73 | - [ sh, -xc, "echo $(date) ': hello world!'" ] 74 | - [ sh, -c, echo "=========hello world=========" ] 75 | - ls -l /root 76 | ``` 77 | 78 | 79 | 80 | ## Schema 81 | 82 | ### Required 83 | 84 | - `part` (Block List) A nested block type which adds a file to the generated cloud-init configuration. Use multiple `part` blocks to specify multiple files, which will be included in order of declaration in the final MIME document. (see [below for nested schema](#nestedblock--part)) 85 | 86 | ### Optional 87 | 88 | - `base64_encode` (Boolean) Specify whether or not to base64 encode the `rendered` output. Defaults to `true`, and cannot be disabled if gzip is `true`. 89 | - `boundary` (String) Specify the Writer's default boundary separator. Defaults to `MIMEBOUNDARY`. 90 | - `gzip` (Boolean) Specify whether or not to gzip the `rendered` output. Defaults to `true`. 91 | 92 | ### Read-Only 93 | 94 | - `id` (String) [CRC-32](https://pkg.go.dev/hash/crc32) checksum of `rendered` cloud-init config. 95 | - `rendered` (String) The final rendered multi-part cloud-init config. 96 | 97 | 98 | ### Nested Schema for `part` 99 | 100 | Required: 101 | 102 | - `content` (String) Body content for the part. 103 | 104 | Optional: 105 | 106 | - `content_type` (String) A MIME-style content type to report in the header for the part. Defaults to `text/plain` 107 | - `filename` (String) A filename to report in the header for the part. 108 | - `merge_type` (String) A value for the `X-Merge-Type` header of the part, to control [cloud-init merging behavior](https://cloudinit.readthedocs.io/en/latest/reference/merging.html). 109 | 110 | -------------------------------------------------------------------------------- /docs/cdktf/typescript/data-sources/config.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "cloudinit_config Data Source - terraform-provider-cloudinit" 3 | description: |- 4 | Renders a multi-part MIME configuration https://cloudinit.readthedocs.io/en/latest/explanation/format.html#mime-multi-part-archive for use with cloud-init https://cloudinit.readthedocs.io/en/latest/. 5 | Cloud-init is a commonly-used startup configuration utility for cloud compute instances. It accepts configuration via provider-specific user data mechanisms, such as user_data for Amazon EC2 instances. Multi-part MIME is one of the data formats it accepts. For more information, see User-Data Formats https://cloudinit.readthedocs.io/en/latest/explanation/format.html in the cloud-init manual. 6 | This is not a generalized utility for producing multi-part MIME messages. Its feature set is specialized for cloud-init multi-part MIME messages. 7 | --- 8 | 9 | 10 | 11 | # cloudinit_config (Data Source) 12 | 13 | Renders a [multi-part MIME configuration](https://cloudinit.readthedocs.io/en/latest/explanation/format.html#mime-multi-part-archive) for use with [cloud-init](https://cloudinit.readthedocs.io/en/latest/). 14 | 15 | Cloud-init is a commonly-used startup configuration utility for cloud compute instances. It accepts configuration via provider-specific user data mechanisms, such as `userData` for Amazon EC2 instances. Multi-part MIME is one of the data formats it accepts. For more information, see [User-Data Formats](https://cloudinit.readthedocs.io/en/latest/explanation/format.html) in the cloud-init manual. 16 | 17 | This is not a generalized utility for producing multi-part MIME messages. Its feature set is specialized for cloud-init multi-part MIME messages. 18 | 19 | ## Example Usage 20 | 21 | ### Config 22 | ```typescript 23 | // DO NOT EDIT. Code generated by 'cdktf convert' - Please report bugs at https://cdk.tf/bug 24 | import { Construct } from "constructs"; 25 | import { Fn, Token, TerraformStack } from "cdktf"; 26 | /* 27 | * Provider bindings are generated by running `cdktf get`. 28 | * See https://cdk.tf/provider-generation for more details. 29 | */ 30 | import { DataCloudinitConfig } from "./.gen/providers/cloudinit/data-cloudinit-config"; 31 | class MyConvertedCode extends TerraformStack { 32 | constructor(scope: Construct, name: string) { 33 | super(scope, name); 34 | new DataCloudinitConfig(this, "foobar", { 35 | base64Encode: false, 36 | gzip: false, 37 | part: [ 38 | { 39 | content: Token.asString(Fn.file("${path.module}/hello-script.sh")), 40 | contentType: "text/x-shellscript", 41 | filename: "hello-script.sh", 42 | }, 43 | { 44 | content: Token.asString(Fn.file("${path.module}/cloud-config.yaml")), 45 | contentType: "text/cloud-config", 46 | filename: "cloud-config.yaml", 47 | }, 48 | ], 49 | }); 50 | } 51 | } 52 | 53 | ``` 54 | 55 | ### hello-script.sh 56 | ```shell 57 | #!/bin/sh 58 | echo "Hello World! I'm starting up now at $(date -R)!" 59 | ``` 60 | 61 | ### cloud-config.yaml 62 | ```yaml 63 | #cloud-config 64 | # See documentation for more configuration examples 65 | # https://cloudinit.readthedocs.io/en/latest/reference/examples.html 66 | 67 | # Install arbitrary packages 68 | # https://cloudinit.readthedocs.io/en/latest/reference/examples.html#install-arbitrary-packages 69 | packages: 70 | - python 71 | # Run commands on first boot 72 | # https://cloudinit.readthedocs.io/en/latest/reference/examples.html#run-commands-on-first-boot 73 | runcmd: 74 | - [ ls, -l, / ] 75 | - [ sh, -xc, "echo $(date) ': hello world!'" ] 76 | - [ sh, -c, echo "=========hello world=========" ] 77 | - ls -l /root 78 | ``` 79 | 80 | 81 | 82 | ## Schema 83 | 84 | ### Required 85 | 86 | - `part` (Block List) A nested block type which adds a file to the generated cloud-init configuration. Use multiple `part` blocks to specify multiple files, which will be included in order of declaration in the final MIME document. (see [below for nested schema](#nestedblock--part)) 87 | 88 | ### Optional 89 | 90 | - `base64Encode` (Boolean) Specify whether or not to base64 encode the `rendered` output. Defaults to `true`, and cannot be disabled if gzip is `true`. 91 | - `boundary` (String) Specify the Writer's default boundary separator. Defaults to `MIMEBOUNDARY`. 92 | - `gzip` (Boolean) Specify whether or not to gzip the `rendered` output. Defaults to `true`. 93 | 94 | ### Read-Only 95 | 96 | - `id` (String) [CRC-32](https://pkg.go.dev/hash/crc32) checksum of `rendered` cloud-init config. 97 | - `rendered` (String) The final rendered multi-part cloud-init config. 98 | 99 | 100 | ### Nested Schema for `part` 101 | 102 | Required: 103 | 104 | - `content` (String) Body content for the part. 105 | 106 | Optional: 107 | 108 | - `contentType` (String) A MIME-style content type to report in the header for the part. Defaults to `text/plain` 109 | - `filename` (String) A filename to report in the header for the part. 110 | - `mergeType` (String) A value for the `X-Merge-Type` header of the part, to control [cloud-init merging behavior](https://cloudinit.readthedocs.io/en/latest/reference/merging.html). 111 | 112 | -------------------------------------------------------------------------------- /docs/cdktf/typescript/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "cloudinit Provider" 3 | description: |- 4 | The cloud-init Terraform provider exposes the cloudinit_config data source, previously available as the template_cloudinit_config resource in the template provider https://registry.terraform.io/providers/hashicorp/template/latest/docs/data-sources/cloudinit_config, which renders a multipart MIME configuration https://cloudinit.readthedocs.io/en/latest/explanation/format.html#mime-multi-part-archive for use with cloud-init https://cloudinit.readthedocs.io/en/latest/. 5 | --- 6 | 7 | 8 | 9 | # cloudinit Provider 10 | 11 | The cloud-init Terraform provider exposes the `cloudinit_config` data source, previously available as the `template_cloudinit_config` resource [in the template provider](https://registry.terraform.io/providers/hashicorp/template/latest/docs/data-sources/cloudinit_config), which renders a [multipart MIME configuration](https://cloudinit.readthedocs.io/en/latest/explanation/format.html#mime-multi-part-archive) for use with [cloud-init](https://cloudinit.readthedocs.io/en/latest/). 12 | 13 | This provider requires no configuration. For information on the resources it provides, see the navigation bar. 14 | -------------------------------------------------------------------------------- /docs/cdktf/typescript/resources/config.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "cloudinit_config Resource - terraform-provider-cloudinit" 3 | description: |- 4 | Renders a multi-part MIME configuration https://cloudinit.readthedocs.io/en/latest/explanation/format.html#mime-multi-part-archive for use with cloud-init https://cloudinit.readthedocs.io/en/latest/. 5 | Cloud-init is a commonly-used startup configuration utility for cloud compute instances. It accepts configuration via provider-specific user data mechanisms, such as user_data for Amazon EC2 instances. Multi-part MIME is one of the data formats it accepts. For more information, see User-Data Formats https://cloudinit.readthedocs.io/en/latest/explanation/format.html in the cloud-init manual. 6 | This is not a generalized utility for producing multi-part MIME messages. Its feature set is specialized for cloud-init multi-part MIME messages. 7 | --- 8 | 9 | 10 | 11 | # cloudinit_config (Resource) 12 | 13 | ~> **This resource is deprecated** Please use the [cloudinit_config](https://registry.terraform.io/providers/hashicorp/cloudinit/latest/docs/data-sources/config) 14 | data source instead. 15 | 16 | Renders a [multi-part MIME configuration](https://cloudinit.readthedocs.io/en/latest/explanation/format.html#mime-multi-part-archive) for use with [cloud-init](https://cloudinit.readthedocs.io/en/latest/). 17 | 18 | Cloud-init is a commonly-used startup configuration utility for cloud compute instances. It accepts configuration via provider-specific user data mechanisms, such as `userData` for Amazon EC2 instances. Multi-part MIME is one of the data formats it accepts. For more information, see [User-Data Formats](https://cloudinit.readthedocs.io/en/latest/explanation/format.html) in the cloud-init manual. 19 | 20 | This is not a generalized utility for producing multi-part MIME messages. Its feature set is specialized for cloud-init multi-part MIME messages. 21 | 22 | ## Example Usage 23 | 24 | ### Config 25 | ```typescript 26 | // DO NOT EDIT. Code generated by 'cdktf convert' - Please report bugs at https://cdk.tf/bug 27 | import { Construct } from "constructs"; 28 | import { Fn, Token, TerraformStack } from "cdktf"; 29 | /* 30 | * Provider bindings are generated by running `cdktf get`. 31 | * See https://cdk.tf/provider-generation for more details. 32 | */ 33 | import { Config } from "./.gen/providers/cloudinit/config"; 34 | class MyConvertedCode extends TerraformStack { 35 | constructor(scope: Construct, name: string) { 36 | super(scope, name); 37 | new Config(this, "foobar", { 38 | base64Encode: false, 39 | gzip: false, 40 | part: [ 41 | { 42 | content: Token.asString(Fn.file("${path.module}/hello-script.sh")), 43 | contentType: "text/x-shellscript", 44 | filename: "hello-script.sh", 45 | }, 46 | { 47 | content: Token.asString(Fn.file("${path.module}/cloud-config.yaml")), 48 | contentType: "text/cloud-config", 49 | filename: "cloud-config.yaml", 50 | }, 51 | ], 52 | }); 53 | } 54 | } 55 | 56 | ``` 57 | 58 | ### hello-script.sh 59 | ```shell 60 | #!/bin/sh 61 | echo "Hello World! I'm starting up now at $(date -R)!" 62 | ``` 63 | 64 | ### cloud-config.yaml 65 | ```yaml 66 | #cloud-config 67 | # See documentation for more configuration examples 68 | # https://cloudinit.readthedocs.io/en/latest/reference/examples.html 69 | 70 | # Install arbitrary packages 71 | # https://cloudinit.readthedocs.io/en/latest/reference/examples.html#install-arbitrary-packages 72 | packages: 73 | - python 74 | # Run commands on first boot 75 | # https://cloudinit.readthedocs.io/en/latest/reference/examples.html#run-commands-on-first-boot 76 | runcmd: 77 | - [ ls, -l, / ] 78 | - [ sh, -xc, "echo $(date) ': hello world!'" ] 79 | - [ sh, -c, echo "=========hello world=========" ] 80 | - ls -l /root 81 | ``` 82 | 83 | 84 | 85 | ## Schema 86 | 87 | ### Required 88 | 89 | - `part` (Block List) A nested block type which adds a file to the generated cloud-init configuration. Use multiple `part` blocks to specify multiple files, which will be included in order of declaration in the final MIME document. (see [below for nested schema](#nestedblock--part)) 90 | 91 | ### Optional 92 | 93 | - `base64Encode` (Boolean) Specify whether or not to base64 encode the `rendered` output. Defaults to `true`, and cannot be disabled if gzip is `true`. 94 | - `boundary` (String) Specify the Writer's default boundary separator. Defaults to `MIMEBOUNDARY`. 95 | - `gzip` (Boolean) Specify whether or not to gzip the `rendered` output. Defaults to `true`. 96 | 97 | ### Read-Only 98 | 99 | - `id` (String) [CRC-32](https://pkg.go.dev/hash/crc32) checksum of `rendered` cloud-init config. 100 | - `rendered` (String) The final rendered multi-part cloud-init config. 101 | 102 | 103 | ### Nested Schema for `part` 104 | 105 | Required: 106 | 107 | - `content` (String) Body content for the part. 108 | 109 | Optional: 110 | 111 | - `contentType` (String) A MIME-style content type to report in the header for the part. Defaults to `text/plain` 112 | - `filename` (String) A filename to report in the header for the part. 113 | - `mergeType` (String) A value for the `X-Merge-Type` header of the part, to control [cloud-init merging behavior](https://cloudinit.readthedocs.io/en/latest/reference/merging.html). 114 | 115 | -------------------------------------------------------------------------------- /docs/data-sources/config.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "cloudinit_config Data Source - terraform-provider-cloudinit" 3 | description: |- 4 | Renders a multi-part MIME configuration https://cloudinit.readthedocs.io/en/latest/explanation/format.html#mime-multi-part-archive for use with cloud-init https://cloudinit.readthedocs.io/en/latest/. 5 | Cloud-init is a commonly-used startup configuration utility for cloud compute instances. It accepts configuration via provider-specific user data mechanisms, such as user_data for Amazon EC2 instances. Multi-part MIME is one of the data formats it accepts. For more information, see User-Data Formats https://cloudinit.readthedocs.io/en/latest/explanation/format.html in the cloud-init manual. 6 | This is not a generalized utility for producing multi-part MIME messages. Its feature set is specialized for cloud-init multi-part MIME messages. 7 | --- 8 | 9 | # cloudinit_config (Data Source) 10 | 11 | Renders a [multi-part MIME configuration](https://cloudinit.readthedocs.io/en/latest/explanation/format.html#mime-multi-part-archive) for use with [cloud-init](https://cloudinit.readthedocs.io/en/latest/). 12 | 13 | Cloud-init is a commonly-used startup configuration utility for cloud compute instances. It accepts configuration via provider-specific user data mechanisms, such as `user_data` for Amazon EC2 instances. Multi-part MIME is one of the data formats it accepts. For more information, see [User-Data Formats](https://cloudinit.readthedocs.io/en/latest/explanation/format.html) in the cloud-init manual. 14 | 15 | This is not a generalized utility for producing multi-part MIME messages. Its feature set is specialized for cloud-init multi-part MIME messages. 16 | 17 | ## Example Usage 18 | 19 | ### Config 20 | ```terraform 21 | data "cloudinit_config" "foobar" { 22 | gzip = false 23 | base64_encode = false 24 | 25 | part { 26 | filename = "hello-script.sh" 27 | content_type = "text/x-shellscript" 28 | 29 | content = file("${path.module}/hello-script.sh") 30 | } 31 | 32 | part { 33 | filename = "cloud-config.yaml" 34 | content_type = "text/cloud-config" 35 | 36 | content = file("${path.module}/cloud-config.yaml") 37 | } 38 | } 39 | ``` 40 | 41 | ### hello-script.sh 42 | ```shell 43 | #!/bin/sh 44 | echo "Hello World! I'm starting up now at $(date -R)!" 45 | ``` 46 | 47 | ### cloud-config.yaml 48 | ```yaml 49 | #cloud-config 50 | # See documentation for more configuration examples 51 | # https://cloudinit.readthedocs.io/en/latest/reference/examples.html 52 | 53 | # Install arbitrary packages 54 | # https://cloudinit.readthedocs.io/en/latest/reference/examples.html#install-arbitrary-packages 55 | packages: 56 | - python 57 | # Run commands on first boot 58 | # https://cloudinit.readthedocs.io/en/latest/reference/examples.html#run-commands-on-first-boot 59 | runcmd: 60 | - [ ls, -l, / ] 61 | - [ sh, -xc, "echo $(date) ': hello world!'" ] 62 | - [ sh, -c, echo "=========hello world=========" ] 63 | - ls -l /root 64 | ``` 65 | 66 | 67 | 68 | ## Schema 69 | 70 | ### Required 71 | 72 | - `part` (Block List) A nested block type which adds a file to the generated cloud-init configuration. Use multiple `part` blocks to specify multiple files, which will be included in order of declaration in the final MIME document. (see [below for nested schema](#nestedblock--part)) 73 | 74 | ### Optional 75 | 76 | - `base64_encode` (Boolean) Specify whether or not to base64 encode the `rendered` output. Defaults to `true`, and cannot be disabled if gzip is `true`. 77 | - `boundary` (String) Specify the Writer's default boundary separator. Defaults to `MIMEBOUNDARY`. 78 | - `gzip` (Boolean) Specify whether or not to gzip the `rendered` output. Defaults to `true`. 79 | 80 | ### Read-Only 81 | 82 | - `id` (String) [CRC-32](https://pkg.go.dev/hash/crc32) checksum of `rendered` cloud-init config. 83 | - `rendered` (String) The final rendered multi-part cloud-init config. 84 | 85 | 86 | ### Nested Schema for `part` 87 | 88 | Required: 89 | 90 | - `content` (String) Body content for the part. 91 | 92 | Optional: 93 | 94 | - `content_type` (String) A MIME-style content type to report in the header for the part. Defaults to `text/plain` 95 | - `filename` (String) A filename to report in the header for the part. 96 | - `merge_type` (String) A value for the `X-Merge-Type` header of the part, to control [cloud-init merging behavior](https://cloudinit.readthedocs.io/en/latest/reference/merging.html). 97 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "cloudinit Provider" 3 | description: |- 4 | The cloud-init Terraform provider exposes the cloudinit_config data source, previously available as the template_cloudinit_config resource in the template provider https://registry.terraform.io/providers/hashicorp/template/latest/docs/data-sources/cloudinit_config, which renders a multipart MIME configuration https://cloudinit.readthedocs.io/en/latest/explanation/format.html#mime-multi-part-archive for use with cloud-init https://cloudinit.readthedocs.io/en/latest/. 5 | --- 6 | 7 | # cloudinit Provider 8 | 9 | The cloud-init Terraform provider exposes the `cloudinit_config` data source, previously available as the `template_cloudinit_config` resource [in the template provider](https://registry.terraform.io/providers/hashicorp/template/latest/docs/data-sources/cloudinit_config), which renders a [multipart MIME configuration](https://cloudinit.readthedocs.io/en/latest/explanation/format.html#mime-multi-part-archive) for use with [cloud-init](https://cloudinit.readthedocs.io/en/latest/). 10 | 11 | This provider requires no configuration. For information on the resources it provides, see the navigation bar. -------------------------------------------------------------------------------- /docs/resources/config.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "cloudinit_config Resource - terraform-provider-cloudinit" 3 | description: |- 4 | Renders a multi-part MIME configuration https://cloudinit.readthedocs.io/en/latest/explanation/format.html#mime-multi-part-archive for use with cloud-init https://cloudinit.readthedocs.io/en/latest/. 5 | Cloud-init is a commonly-used startup configuration utility for cloud compute instances. It accepts configuration via provider-specific user data mechanisms, such as user_data for Amazon EC2 instances. Multi-part MIME is one of the data formats it accepts. For more information, see User-Data Formats https://cloudinit.readthedocs.io/en/latest/explanation/format.html in the cloud-init manual. 6 | This is not a generalized utility for producing multi-part MIME messages. Its feature set is specialized for cloud-init multi-part MIME messages. 7 | --- 8 | 9 | # cloudinit_config (Resource) 10 | 11 | ~> **This resource is deprecated** Please use the [cloudinit_config](https://registry.terraform.io/providers/hashicorp/cloudinit/latest/docs/data-sources/config) 12 | data source instead. 13 | 14 | Renders a [multi-part MIME configuration](https://cloudinit.readthedocs.io/en/latest/explanation/format.html#mime-multi-part-archive) for use with [cloud-init](https://cloudinit.readthedocs.io/en/latest/). 15 | 16 | Cloud-init is a commonly-used startup configuration utility for cloud compute instances. It accepts configuration via provider-specific user data mechanisms, such as `user_data` for Amazon EC2 instances. Multi-part MIME is one of the data formats it accepts. For more information, see [User-Data Formats](https://cloudinit.readthedocs.io/en/latest/explanation/format.html) in the cloud-init manual. 17 | 18 | This is not a generalized utility for producing multi-part MIME messages. Its feature set is specialized for cloud-init multi-part MIME messages. 19 | 20 | ## Example Usage 21 | 22 | ### Config 23 | ```terraform 24 | resource "cloudinit_config" "foobar" { 25 | gzip = false 26 | base64_encode = false 27 | 28 | part { 29 | filename = "hello-script.sh" 30 | content_type = "text/x-shellscript" 31 | 32 | content = file("${path.module}/hello-script.sh") 33 | } 34 | 35 | part { 36 | filename = "cloud-config.yaml" 37 | content_type = "text/cloud-config" 38 | 39 | content = file("${path.module}/cloud-config.yaml") 40 | } 41 | } 42 | ``` 43 | 44 | ### hello-script.sh 45 | ```shell 46 | #!/bin/sh 47 | echo "Hello World! I'm starting up now at $(date -R)!" 48 | ``` 49 | 50 | ### cloud-config.yaml 51 | ```yaml 52 | #cloud-config 53 | # See documentation for more configuration examples 54 | # https://cloudinit.readthedocs.io/en/latest/reference/examples.html 55 | 56 | # Install arbitrary packages 57 | # https://cloudinit.readthedocs.io/en/latest/reference/examples.html#install-arbitrary-packages 58 | packages: 59 | - python 60 | # Run commands on first boot 61 | # https://cloudinit.readthedocs.io/en/latest/reference/examples.html#run-commands-on-first-boot 62 | runcmd: 63 | - [ ls, -l, / ] 64 | - [ sh, -xc, "echo $(date) ': hello world!'" ] 65 | - [ sh, -c, echo "=========hello world=========" ] 66 | - ls -l /root 67 | ``` 68 | 69 | 70 | 71 | ## Schema 72 | 73 | ### Required 74 | 75 | - `part` (Block List) A nested block type which adds a file to the generated cloud-init configuration. Use multiple `part` blocks to specify multiple files, which will be included in order of declaration in the final MIME document. (see [below for nested schema](#nestedblock--part)) 76 | 77 | ### Optional 78 | 79 | - `base64_encode` (Boolean) Specify whether or not to base64 encode the `rendered` output. Defaults to `true`, and cannot be disabled if gzip is `true`. 80 | - `boundary` (String) Specify the Writer's default boundary separator. Defaults to `MIMEBOUNDARY`. 81 | - `gzip` (Boolean) Specify whether or not to gzip the `rendered` output. Defaults to `true`. 82 | 83 | ### Read-Only 84 | 85 | - `id` (String) [CRC-32](https://pkg.go.dev/hash/crc32) checksum of `rendered` cloud-init config. 86 | - `rendered` (String) The final rendered multi-part cloud-init config. 87 | 88 | 89 | ### Nested Schema for `part` 90 | 91 | Required: 92 | 93 | - `content` (String) Body content for the part. 94 | 95 | Optional: 96 | 97 | - `content_type` (String) A MIME-style content type to report in the header for the part. Defaults to `text/plain` 98 | - `filename` (String) A filename to report in the header for the part. 99 | - `merge_type` (String) A value for the `X-Merge-Type` header of the part, to control [cloud-init merging behavior](https://cloudinit.readthedocs.io/en/latest/reference/merging.html). 100 | -------------------------------------------------------------------------------- /examples/data-sources/cloudinit_config/cloud-config.yaml: -------------------------------------------------------------------------------- 1 | #cloud-config 2 | # See documentation for more configuration examples 3 | # https://cloudinit.readthedocs.io/en/latest/reference/examples.html 4 | 5 | # Install arbitrary packages 6 | # https://cloudinit.readthedocs.io/en/latest/reference/examples.html#install-arbitrary-packages 7 | packages: 8 | - python 9 | # Run commands on first boot 10 | # https://cloudinit.readthedocs.io/en/latest/reference/examples.html#run-commands-on-first-boot 11 | runcmd: 12 | - [ ls, -l, / ] 13 | - [ sh, -xc, "echo $(date) ': hello world!'" ] 14 | - [ sh, -c, echo "=========hello world=========" ] 15 | - ls -l /root -------------------------------------------------------------------------------- /examples/data-sources/cloudinit_config/data-source.tf: -------------------------------------------------------------------------------- 1 | data "cloudinit_config" "foobar" { 2 | gzip = false 3 | base64_encode = false 4 | 5 | part { 6 | filename = "hello-script.sh" 7 | content_type = "text/x-shellscript" 8 | 9 | content = file("${path.module}/hello-script.sh") 10 | } 11 | 12 | part { 13 | filename = "cloud-config.yaml" 14 | content_type = "text/cloud-config" 15 | 16 | content = file("${path.module}/cloud-config.yaml") 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/data-sources/cloudinit_config/hello-script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "Hello World! I'm starting up now at $(date -R)!" 3 | -------------------------------------------------------------------------------- /examples/resources/cloudinit_config/cloud-config.yaml: -------------------------------------------------------------------------------- 1 | #cloud-config 2 | # See documentation for more configuration examples 3 | # https://cloudinit.readthedocs.io/en/latest/reference/examples.html 4 | 5 | # Install arbitrary packages 6 | # https://cloudinit.readthedocs.io/en/latest/reference/examples.html#install-arbitrary-packages 7 | packages: 8 | - python 9 | # Run commands on first boot 10 | # https://cloudinit.readthedocs.io/en/latest/reference/examples.html#run-commands-on-first-boot 11 | runcmd: 12 | - [ ls, -l, / ] 13 | - [ sh, -xc, "echo $(date) ': hello world!'" ] 14 | - [ sh, -c, echo "=========hello world=========" ] 15 | - ls -l /root -------------------------------------------------------------------------------- /examples/resources/cloudinit_config/hello-script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "Hello World! I'm starting up now at $(date -R)!" 3 | -------------------------------------------------------------------------------- /examples/resources/cloudinit_config/resource.tf: -------------------------------------------------------------------------------- 1 | resource "cloudinit_config" "foobar" { 2 | gzip = false 3 | base64_encode = false 4 | 5 | part { 6 | filename = "hello-script.sh" 7 | content_type = "text/x-shellscript" 8 | 9 | content = file("${path.module}/hello-script.sh") 10 | } 11 | 12 | part { 13 | filename = "cloud-config.yaml" 14 | content_type = "text/cloud-config" 15 | 16 | content = file("${path.module}/cloud-config.yaml") 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/hashicorp/terraform-provider-cloudinit 2 | 3 | go 1.23.7 4 | 5 | require ( 6 | github.com/hashicorp/terraform-plugin-framework v1.15.0 7 | github.com/hashicorp/terraform-plugin-framework-validators v0.18.0 8 | github.com/hashicorp/terraform-plugin-go v0.28.0 9 | github.com/hashicorp/terraform-plugin-log v0.9.0 10 | github.com/hashicorp/terraform-plugin-testing v1.13.2 11 | ) 12 | 13 | require ( 14 | github.com/ProtonMail/go-crypto v1.1.6 // indirect 15 | github.com/agext/levenshtein v1.2.2 // indirect 16 | github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect 17 | github.com/cloudflare/circl v1.6.1 // indirect 18 | github.com/fatih/color v1.16.0 // indirect 19 | github.com/golang/protobuf v1.5.4 // indirect 20 | github.com/google/go-cmp v0.7.0 // indirect 21 | github.com/hashicorp/errwrap v1.1.0 // indirect 22 | github.com/hashicorp/go-checkpoint v0.5.0 // indirect 23 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect 24 | github.com/hashicorp/go-cty v1.5.0 // indirect 25 | github.com/hashicorp/go-hclog v1.6.3 // indirect 26 | github.com/hashicorp/go-multierror v1.1.1 // indirect 27 | github.com/hashicorp/go-plugin v1.6.3 // indirect 28 | github.com/hashicorp/go-retryablehttp v0.7.7 // indirect 29 | github.com/hashicorp/go-uuid v1.0.3 // indirect 30 | github.com/hashicorp/go-version v1.7.0 // indirect 31 | github.com/hashicorp/hc-install v0.9.2 // indirect 32 | github.com/hashicorp/hcl/v2 v2.23.0 // indirect 33 | github.com/hashicorp/logutils v1.0.0 // indirect 34 | github.com/hashicorp/terraform-exec v0.23.0 // indirect 35 | github.com/hashicorp/terraform-json v0.25.0 // indirect 36 | github.com/hashicorp/terraform-plugin-sdk/v2 v2.37.0 // indirect 37 | github.com/hashicorp/terraform-registry-address v0.2.5 // indirect 38 | github.com/hashicorp/terraform-svchost v0.1.1 // indirect 39 | github.com/hashicorp/yamux v0.1.1 // indirect 40 | github.com/kr/pretty v0.3.0 // indirect 41 | github.com/mattn/go-colorable v0.1.13 // indirect 42 | github.com/mattn/go-isatty v0.0.20 // indirect 43 | github.com/mitchellh/copystructure v1.2.0 // indirect 44 | github.com/mitchellh/go-testing-interface v1.14.1 // indirect 45 | github.com/mitchellh/go-wordwrap v1.0.0 // indirect 46 | github.com/mitchellh/mapstructure v1.5.0 // indirect 47 | github.com/mitchellh/reflectwalk v1.0.2 // indirect 48 | github.com/oklog/run v1.0.0 // indirect 49 | github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect 50 | github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect 51 | github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect 52 | github.com/zclconf/go-cty v1.16.3 // indirect 53 | golang.org/x/crypto v0.39.0 // indirect 54 | golang.org/x/mod v0.25.0 // indirect 55 | golang.org/x/net v0.40.0 // indirect 56 | golang.org/x/sync v0.15.0 // indirect 57 | golang.org/x/sys v0.33.0 // indirect 58 | golang.org/x/text v0.26.0 // indirect 59 | golang.org/x/tools v0.33.0 // indirect 60 | google.golang.org/appengine v1.6.8 // indirect 61 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect 62 | google.golang.org/grpc v1.72.1 // indirect 63 | google.golang.org/protobuf v1.36.6 // indirect 64 | ) 65 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= 2 | dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= 3 | github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= 4 | github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= 5 | github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= 6 | github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= 7 | github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE= 8 | github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= 9 | github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= 10 | github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= 11 | github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= 12 | github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= 13 | github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= 14 | github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= 15 | github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= 16 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 17 | github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= 18 | github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= 19 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 20 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 21 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 22 | github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= 23 | github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= 24 | github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= 25 | github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= 26 | github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= 27 | github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= 28 | github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= 29 | github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= 30 | github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= 31 | github.com/go-git/go-git/v5 v5.14.0 h1:/MD3lCrGjCen5WfEAzKg00MJJffKhC8gzS80ycmCi60= 32 | github.com/go-git/go-git/v5 v5.14.0/go.mod h1:Z5Xhoia5PcWA3NF8vRLURn9E5FRhSl7dGj9ItW3Wk5k= 33 | github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= 34 | github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 35 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 36 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 37 | github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= 38 | github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= 39 | github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= 40 | github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= 41 | github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 42 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 43 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 44 | github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= 45 | github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 46 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 47 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 48 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 49 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 50 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 51 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 52 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 53 | github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= 54 | github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 55 | github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU= 56 | github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg= 57 | github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 58 | github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= 59 | github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= 60 | github.com/hashicorp/go-cty v1.5.0 h1:EkQ/v+dDNUqnuVpmS5fPqyY71NXVgT5gf32+57xY8g0= 61 | github.com/hashicorp/go-cty v1.5.0/go.mod h1:lFUCG5kd8exDobgSfyj4ONE/dc822kiYMguVKdHGMLM= 62 | github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= 63 | github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= 64 | github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= 65 | github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= 66 | github.com/hashicorp/go-plugin v1.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0UUrwg= 67 | github.com/hashicorp/go-plugin v1.6.3/go.mod h1:MRobyh+Wc/nYy1V4KAXUiYfzxoYhs7V1mlH1Z7iY2h0= 68 | github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= 69 | github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= 70 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 71 | github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= 72 | github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 73 | github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= 74 | github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 75 | github.com/hashicorp/hc-install v0.9.2 h1:v80EtNX4fCVHqzL9Lg/2xkp62bbvQMnvPQ0G+OmtO24= 76 | github.com/hashicorp/hc-install v0.9.2/go.mod h1:XUqBQNnuT4RsxoxiM9ZaUk0NX8hi2h+Lb6/c0OZnC/I= 77 | github.com/hashicorp/hcl/v2 v2.23.0 h1:Fphj1/gCylPxHutVSEOf2fBOh1VE4AuLV7+kbJf3qos= 78 | github.com/hashicorp/hcl/v2 v2.23.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA= 79 | github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= 80 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 81 | github.com/hashicorp/terraform-exec v0.23.0 h1:MUiBM1s0CNlRFsCLJuM5wXZrzA3MnPYEsiXmzATMW/I= 82 | github.com/hashicorp/terraform-exec v0.23.0/go.mod h1:mA+qnx1R8eePycfwKkCRk3Wy65mwInvlpAeOwmA7vlY= 83 | github.com/hashicorp/terraform-json v0.25.0 h1:rmNqc/CIfcWawGiwXmRuiXJKEiJu1ntGoxseG1hLhoQ= 84 | github.com/hashicorp/terraform-json v0.25.0/go.mod h1:sMKS8fiRDX4rVlR6EJUMudg1WcanxCMoWwTLkgZP/vc= 85 | github.com/hashicorp/terraform-plugin-framework v1.15.0 h1:LQ2rsOfmDLxcn5EeIwdXFtr03FVsNktbbBci8cOKdb4= 86 | github.com/hashicorp/terraform-plugin-framework v1.15.0/go.mod h1:hxrNI/GY32KPISpWqlCoTLM9JZsGH3CyYlir09bD/fI= 87 | github.com/hashicorp/terraform-plugin-framework-validators v0.18.0 h1:OQnlOt98ua//rCw+QhBbSqfW3QbwtVrcdWeQN5gI3Hw= 88 | github.com/hashicorp/terraform-plugin-framework-validators v0.18.0/go.mod h1:lZvZvagw5hsJwuY7mAY6KUz45/U6fiDR0CzQAwWD0CA= 89 | github.com/hashicorp/terraform-plugin-go v0.28.0 h1:zJmu2UDwhVN0J+J20RE5huiF3XXlTYVIleaevHZgKPA= 90 | github.com/hashicorp/terraform-plugin-go v0.28.0/go.mod h1:FDa2Bb3uumkTGSkTFpWSOwWJDwA7bf3vdP3ltLDTH6o= 91 | github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= 92 | github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= 93 | github.com/hashicorp/terraform-plugin-sdk/v2 v2.37.0 h1:NFPMacTrY/IdcIcnUB+7hsore1ZaRWU9cnB6jFoBnIM= 94 | github.com/hashicorp/terraform-plugin-sdk/v2 v2.37.0/go.mod h1:QYmYnLfsosrxjCnGY1p9c7Zj6n9thnEE+7RObeYs3fA= 95 | github.com/hashicorp/terraform-plugin-testing v1.13.2 h1:mSotG4Odl020vRjIenA3rggwo6Kg6XCKIwtRhYgp+/M= 96 | github.com/hashicorp/terraform-plugin-testing v1.13.2/go.mod h1:WHQ9FDdiLoneey2/QHpGM/6SAYf4A7AZazVg7230pLE= 97 | github.com/hashicorp/terraform-registry-address v0.2.5 h1:2GTftHqmUhVOeuu9CW3kwDkRe4pcBDq0uuK5VJngU1M= 98 | github.com/hashicorp/terraform-registry-address v0.2.5/go.mod h1:PpzXWINwB5kuVS5CA7m1+eO2f1jKb5ZDIxrOPfpnGkg= 99 | github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ= 100 | github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc= 101 | github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= 102 | github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= 103 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= 104 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= 105 | github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= 106 | github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= 107 | github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= 108 | github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= 109 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 110 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= 111 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 112 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 113 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 114 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 115 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 116 | github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 117 | github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= 118 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 119 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 120 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 121 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 122 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 123 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 124 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 125 | github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= 126 | github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= 127 | github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= 128 | github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= 129 | github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= 130 | github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= 131 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 132 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 133 | github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= 134 | github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 135 | github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= 136 | github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= 137 | github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= 138 | github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= 139 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 140 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 141 | github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= 142 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= 143 | github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= 144 | github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= 145 | github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= 146 | github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= 147 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 148 | github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= 149 | github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= 150 | github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 151 | github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= 152 | github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= 153 | github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= 154 | github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= 155 | github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= 156 | github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= 157 | github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= 158 | github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= 159 | github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= 160 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 161 | github.com/zclconf/go-cty v1.16.3 h1:osr++gw2T61A8KVYHoQiFbFd1Lh3JOCXc/jFLJXKTxk= 162 | github.com/zclconf/go-cty v1.16.3/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= 163 | github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= 164 | github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= 165 | go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= 166 | go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= 167 | go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= 168 | go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= 169 | go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= 170 | go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= 171 | go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= 172 | go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= 173 | go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= 174 | go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= 175 | go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= 176 | go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= 177 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 178 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 179 | golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= 180 | golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= 181 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 182 | golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= 183 | golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= 184 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 185 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 186 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 187 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 188 | golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= 189 | golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= 190 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 191 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 192 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 193 | golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= 194 | golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 195 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 196 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 197 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 198 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 199 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 200 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 201 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 202 | golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 203 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 204 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 205 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 206 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 207 | golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= 208 | golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 209 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 210 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 211 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 212 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 213 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 214 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= 215 | golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= 216 | golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= 217 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 218 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 219 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 220 | golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= 221 | golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= 222 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 223 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 224 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 225 | google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= 226 | google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= 227 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4= 228 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ= 229 | google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= 230 | google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= 231 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 232 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 233 | google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= 234 | google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= 235 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 236 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 237 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 238 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 239 | gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= 240 | gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= 241 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 242 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 243 | -------------------------------------------------------------------------------- /internal/hashcode/hashcode.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package hashcode 5 | 6 | import ( 7 | "bytes" 8 | "fmt" 9 | "hash/crc32" 10 | ) 11 | 12 | // String hashes a string to a unique hashcode. 13 | // 14 | // crc32 returns a uint32, but for our use we need 15 | // and non negative integer. Here we cast to an integer 16 | // and invert it if the result is negative. 17 | func String(s string) int { 18 | v := int(crc32.ChecksumIEEE([]byte(s))) 19 | if v >= 0 { 20 | return v 21 | } 22 | if -v >= 0 { 23 | return -v 24 | } 25 | // v == MinInt 26 | return 0 27 | } 28 | 29 | // Strings hashes a list of strings to a unique hashcode. 30 | func Strings(strings []string) string { 31 | var buf bytes.Buffer 32 | 33 | for _, s := range strings { 34 | buf.WriteString(fmt.Sprintf("%s-", s)) 35 | } 36 | 37 | return fmt.Sprintf("%d", String(buf.String())) 38 | } 39 | -------------------------------------------------------------------------------- /internal/hashcode/hashcode_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package hashcode 5 | 6 | import ( 7 | "testing" 8 | ) 9 | 10 | func TestString(t *testing.T) { 11 | v := "hello, world" 12 | expected := String(v) 13 | for i := 0; i < 100; i++ { 14 | actual := String(v) 15 | if actual != expected { 16 | t.Fatalf("bad: %#v\n\t%#v", actual, expected) 17 | } 18 | } 19 | } 20 | 21 | func TestStrings(t *testing.T) { 22 | v := []string{"hello", ",", "world"} 23 | expected := Strings(v) 24 | for i := 0; i < 100; i++ { 25 | actual := Strings(v) 26 | if actual != expected { 27 | t.Fatalf("bad: %#v\n\t%#v", actual, expected) 28 | } 29 | } 30 | } 31 | 32 | func TestString_positiveIndex(t *testing.T) { 33 | // "2338615298" hashes to uint32(2147483648) which is math.MinInt32 34 | ips := []string{"192.168.1.3", "192.168.1.5", "2338615298"} 35 | for _, ip := range ips { 36 | if index := String(ip); index < 0 { 37 | t.Fatalf("Bad Index %#v for ip %s", index, ip) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /internal/provider/cloudinit_config.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package provider 5 | 6 | import ( 7 | "bytes" 8 | "compress/gzip" 9 | "context" 10 | "encoding/base64" 11 | "fmt" 12 | "io" 13 | "mime/multipart" 14 | "net/textproto" 15 | "strconv" 16 | 17 | "github.com/hashicorp/terraform-plugin-framework/diag" 18 | "github.com/hashicorp/terraform-plugin-framework/path" 19 | "github.com/hashicorp/terraform-plugin-framework/types" 20 | "github.com/hashicorp/terraform-plugin-log/tflog" 21 | 22 | "github.com/hashicorp/terraform-provider-cloudinit/internal/hashcode" 23 | ) 24 | 25 | // Model and functionality of data source and resource are equivalent. 26 | type configModel struct { 27 | ID types.String `tfsdk:"id"` 28 | Parts types.List `tfsdk:"part"` // configPartModel 29 | Gzip types.Bool `tfsdk:"gzip"` 30 | Base64Encode types.Bool `tfsdk:"base64_encode"` 31 | Boundary types.String `tfsdk:"boundary"` 32 | Rendered types.String `tfsdk:"rendered"` 33 | } 34 | 35 | type configPartModel struct { 36 | ContentType types.String `tfsdk:"content_type"` 37 | Content types.String `tfsdk:"content"` 38 | FileName types.String `tfsdk:"filename"` 39 | MergeType types.String `tfsdk:"merge_type"` 40 | } 41 | 42 | func (c *configModel) setDefaults(ctx context.Context) diag.Diagnostics { 43 | var diags diag.Diagnostics 44 | 45 | if c.Gzip.IsNull() { 46 | c.Gzip = types.BoolValue(true) 47 | } 48 | if c.Base64Encode.IsNull() { 49 | c.Base64Encode = types.BoolValue(true) 50 | } 51 | if c.Boundary.IsNull() { 52 | c.Boundary = types.StringValue("MIMEBOUNDARY") 53 | } 54 | 55 | if c.Parts.IsNull() || c.Parts.IsUnknown() { 56 | return diags 57 | } 58 | 59 | var configParts []configPartModel 60 | diags.Append(c.Parts.ElementsAs(ctx, &configParts, false)...) 61 | if diags.HasError() { 62 | return diags 63 | } 64 | 65 | for i, part := range configParts { 66 | if part.ContentType.IsNull() || part.ContentType.ValueString() == "" { 67 | configParts[i].ContentType = types.StringValue("text/plain") 68 | } 69 | } 70 | 71 | partsList, convertDiags := types.ListValueFrom(ctx, c.Parts.ElementType(ctx), configParts) 72 | diags.Append(convertDiags...) 73 | if diags.HasError() { 74 | return diags 75 | } 76 | 77 | c.Parts = partsList 78 | 79 | return diags 80 | } 81 | 82 | func (c configModel) validate(ctx context.Context) diag.Diagnostics { 83 | var diags diag.Diagnostics 84 | 85 | if c.Gzip.IsUnknown() || c.Base64Encode.IsUnknown() { 86 | return diags 87 | } 88 | diags.Append(c.setDefaults(ctx)...) 89 | if diags.HasError() { 90 | return diags 91 | } 92 | 93 | if c.Gzip.ValueBool() && !c.Base64Encode.ValueBool() { 94 | diags.AddAttributeError( 95 | path.Root("base64_encode"), 96 | "Invalid Attribute Configuration", 97 | "Expected base64_encode to be set to true when gzip is true.", 98 | ) 99 | } 100 | 101 | return diags 102 | } 103 | 104 | func (c *configModel) update(ctx context.Context) diag.Diagnostics { 105 | var buffer bytes.Buffer 106 | var diags diag.Diagnostics 107 | var err error 108 | 109 | // cloudinit Provider 'v2.2.0' doesn't actually set default values in state properly, so we need to make sure 110 | // that we don't use any known empty values from previous versions of state 111 | diags.Append(c.setDefaults(ctx)...) 112 | if diags.HasError() { 113 | return diags 114 | } 115 | 116 | var configParts []configPartModel 117 | diags.Append(c.Parts.ElementsAs(ctx, &configParts, false)...) 118 | if diags.HasError() { 119 | return diags 120 | } 121 | 122 | if c.Gzip.ValueBool() { 123 | gzipWriter := gzip.NewWriter(&buffer) 124 | 125 | err = renderPartsToWriter(ctx, c.Boundary.ValueString(), configParts, gzipWriter) 126 | 127 | gzipWriter.Close() 128 | } else { 129 | err = renderPartsToWriter(ctx, c.Boundary.ValueString(), configParts, &buffer) 130 | } 131 | 132 | if err != nil { 133 | diags.AddError("Unable to render cloudinit config to MIME multi-part file", err.Error()) 134 | return diags 135 | } 136 | 137 | output := "" 138 | if c.Base64Encode.ValueBool() { 139 | output = base64.StdEncoding.EncodeToString(buffer.Bytes()) 140 | } else { 141 | output = buffer.String() 142 | } 143 | 144 | c.ID = types.StringValue(strconv.Itoa(hashcode.String(output))) 145 | c.Rendered = types.StringValue(output) 146 | 147 | return diags 148 | } 149 | 150 | func renderPartsToWriter(ctx context.Context, mimeBoundary string, parts []configPartModel, writer io.Writer) error { 151 | mimeWriter := multipart.NewWriter(writer) 152 | defer func() { 153 | err := mimeWriter.Close() 154 | if err != nil { 155 | tflog.Warn(ctx, fmt.Sprintf("error closing mimeWriter: %s", err)) 156 | } 157 | }() 158 | 159 | // we need to set the boundary explicitly, otherwise the boundary is random 160 | // and this causes terraform to complain about the resource being different 161 | if err := mimeWriter.SetBoundary(mimeBoundary); err != nil { 162 | return err 163 | } 164 | 165 | _, err := writer.Write([]byte(fmt.Sprintf("Content-Type: multipart/mixed; boundary=\"%s\"\n", mimeWriter.Boundary()))) 166 | if err != nil { 167 | return err 168 | } 169 | 170 | _, err = writer.Write([]byte("MIME-Version: 1.0\r\n\r\n")) 171 | if err != nil { 172 | return err 173 | } 174 | 175 | for _, part := range parts { 176 | header := textproto.MIMEHeader{} 177 | 178 | header.Set("Content-Type", part.ContentType.ValueString()) 179 | header.Set("MIME-Version", "1.0") 180 | header.Set("Content-Transfer-Encoding", "7bit") 181 | 182 | if part.FileName.ValueString() != "" { 183 | header.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, part.FileName.ValueString())) 184 | } 185 | 186 | if part.MergeType.ValueString() != "" { 187 | header.Set("X-Merge-Type", part.MergeType.ValueString()) 188 | } 189 | 190 | partWriter, err := mimeWriter.CreatePart(header) 191 | if err != nil { 192 | return err 193 | } 194 | 195 | _, err = partWriter.Write([]byte(part.Content.ValueString())) 196 | if err != nil { 197 | return err 198 | } 199 | } 200 | 201 | return nil 202 | } 203 | -------------------------------------------------------------------------------- /internal/provider/data_source_cloudinit_config.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package provider 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" 10 | "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" 11 | "github.com/hashicorp/terraform-plugin-framework/datasource" 12 | "github.com/hashicorp/terraform-plugin-framework/datasource/schema" 13 | "github.com/hashicorp/terraform-plugin-framework/schema/validator" 14 | ) 15 | 16 | var ( 17 | _ datasource.DataSourceWithValidateConfig = (*configDataSource)(nil) 18 | ) 19 | 20 | type configDataSource struct{} 21 | 22 | func (d *configDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { 23 | resp.TypeName = req.ProviderTypeName + "_config" 24 | } 25 | 26 | func (d *configDataSource) ValidateConfig(ctx context.Context, req datasource.ValidateConfigRequest, resp *datasource.ValidateConfigResponse) { 27 | var cloudinitConfig configModel 28 | 29 | resp.Diagnostics.Append(req.Config.Get(ctx, &cloudinitConfig)...) 30 | if resp.Diagnostics.HasError() { 31 | return 32 | } 33 | 34 | resp.Diagnostics.Append(cloudinitConfig.validate(ctx)...) 35 | } 36 | 37 | func (d *configDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { 38 | resp.Schema = schema.Schema{ 39 | Blocks: map[string]schema.Block{ 40 | "part": schema.ListNestedBlock{ 41 | Validators: []validator.List{ 42 | listvalidator.IsRequired(), 43 | }, 44 | NestedObject: schema.NestedBlockObject{ 45 | Attributes: map[string]schema.Attribute{ 46 | "content_type": schema.StringAttribute{ 47 | Validators: []validator.String{ 48 | stringvalidator.LengthAtLeast(1), 49 | }, 50 | Optional: true, 51 | Computed: true, 52 | MarkdownDescription: "A MIME-style content type to report in the header for the part. Defaults to `text/plain`", 53 | }, 54 | "content": schema.StringAttribute{ 55 | Required: true, 56 | MarkdownDescription: "Body content for the part.", 57 | }, 58 | "filename": schema.StringAttribute{ 59 | Optional: true, 60 | MarkdownDescription: "A filename to report in the header for the part.", 61 | }, 62 | "merge_type": schema.StringAttribute{ 63 | Optional: true, 64 | MarkdownDescription: "A value for the `X-Merge-Type` header of the part, to control " + 65 | "[cloud-init merging behavior](https://cloudinit.readthedocs.io/en/latest/reference/merging.html).", 66 | }, 67 | }, 68 | }, 69 | MarkdownDescription: "A nested block type which adds a file to the generated cloud-init configuration. Use multiple " + 70 | "`part` blocks to specify multiple files, which will be included in order of declaration in the final MIME document.", 71 | }, 72 | }, 73 | Attributes: map[string]schema.Attribute{ 74 | "gzip": schema.BoolAttribute{ 75 | Optional: true, 76 | Computed: true, 77 | MarkdownDescription: "Specify whether or not to gzip the `rendered` output. Defaults to `true`.", 78 | }, 79 | "base64_encode": schema.BoolAttribute{ 80 | Optional: true, 81 | Computed: true, 82 | MarkdownDescription: "Specify whether or not to base64 encode the `rendered` output. Defaults to `true`, and cannot be disabled if gzip is `true`.", 83 | }, 84 | "boundary": schema.StringAttribute{ 85 | Validators: []validator.String{ 86 | stringvalidator.LengthAtLeast(1), 87 | }, 88 | Optional: true, 89 | Computed: true, 90 | MarkdownDescription: "Specify the Writer's default boundary separator. Defaults to `MIMEBOUNDARY`.", 91 | }, 92 | "rendered": schema.StringAttribute{ 93 | Computed: true, 94 | MarkdownDescription: "The final rendered multi-part cloud-init config.", 95 | }, 96 | "id": schema.StringAttribute{ 97 | Computed: true, 98 | MarkdownDescription: "[CRC-32](https://pkg.go.dev/hash/crc32) checksum of `rendered` cloud-init config.", 99 | }, 100 | }, 101 | MarkdownDescription: "Renders a [multi-part MIME configuration](https://cloudinit.readthedocs.io/en/latest/explanation/format.html#mime-multi-part-archive) " + 102 | "for use with [cloud-init](https://cloudinit.readthedocs.io/en/latest/).\n\n" + 103 | "Cloud-init is a commonly-used startup configuration utility for cloud compute instances. It accepts configuration via provider-specific " + 104 | "user data mechanisms, such as `user_data` for Amazon EC2 instances. Multi-part MIME is one of the data formats it accepts. For more information, " + 105 | "see [User-Data Formats](https://cloudinit.readthedocs.io/en/latest/explanation/format.html) in the cloud-init manual.\n\n" + 106 | "This is not a generalized utility for producing multi-part MIME messages. Its feature set is specialized for cloud-init multi-part MIME messages.", 107 | } 108 | } 109 | 110 | func (d *configDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { 111 | var cloudinitConfig configModel 112 | 113 | resp.Diagnostics.Append(req.Config.Get(ctx, &cloudinitConfig)...) 114 | if resp.Diagnostics.HasError() { 115 | return 116 | } 117 | 118 | resp.Diagnostics.Append(cloudinitConfig.update(ctx)...) 119 | resp.Diagnostics.Append(resp.State.Set(ctx, cloudinitConfig)...) 120 | } 121 | -------------------------------------------------------------------------------- /internal/provider/data_source_cloudinit_config_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package provider 5 | 6 | import ( 7 | "regexp" 8 | "testing" 9 | 10 | r "github.com/hashicorp/terraform-plugin-testing/helper/resource" 11 | ) 12 | 13 | func TestConfigDataSourceRender(t *testing.T) { 14 | testCases := []struct { 15 | Name string 16 | DataSourceBlock string 17 | Expected string 18 | }{ 19 | { 20 | "no gzip or b64 - basic content", 21 | `data "cloudinit_config" "foo" { 22 | gzip = false 23 | base64_encode = false 24 | 25 | part { 26 | content_type = "text/x-shellscript" 27 | content = "baz" 28 | } 29 | }`, 30 | "Content-Type: multipart/mixed; boundary=\"MIMEBOUNDARY\"\nMIME-Version: 1.0\r\n\r\n--MIMEBOUNDARY\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/x-shellscript\r\nMime-Version: 1.0\r\n\r\nbaz\r\n--MIMEBOUNDARY--\r\n", 31 | }, 32 | { 33 | "no gzip or b64 - basic content - default to text plain", 34 | `data "cloudinit_config" "foo" { 35 | gzip = false 36 | base64_encode = false 37 | 38 | part { 39 | content = "baz" 40 | } 41 | }`, 42 | "Content-Type: multipart/mixed; boundary=\"MIMEBOUNDARY\"\nMIME-Version: 1.0\r\n\r\n--MIMEBOUNDARY\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/plain\r\nMime-Version: 1.0\r\n\r\nbaz\r\n--MIMEBOUNDARY--\r\n", 43 | }, 44 | { 45 | "no gzip or b64 - content with filename", 46 | `data "cloudinit_config" "foo" { 47 | gzip = false 48 | base64_encode = false 49 | 50 | part { 51 | content_type = "text/x-shellscript" 52 | content = "baz" 53 | filename = "foobar.sh" 54 | } 55 | }`, 56 | "Content-Type: multipart/mixed; boundary=\"MIMEBOUNDARY\"\nMIME-Version: 1.0\r\n\r\n--MIMEBOUNDARY\r\nContent-Disposition: attachment; filename=\"foobar.sh\"\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/x-shellscript\r\nMime-Version: 1.0\r\n\r\nbaz\r\n--MIMEBOUNDARY--\r\n", 57 | }, 58 | { 59 | "no gzip or b64 - two parts, basic content", 60 | `data "cloudinit_config" "foo" { 61 | gzip = false 62 | base64_encode = false 63 | 64 | part { 65 | content_type = "text/x-shellscript" 66 | content = "baz" 67 | } 68 | part { 69 | content_type = "text/x-shellscript" 70 | content = "ffbaz" 71 | } 72 | }`, 73 | "Content-Type: multipart/mixed; boundary=\"MIMEBOUNDARY\"\nMIME-Version: 1.0\r\n\r\n--MIMEBOUNDARY\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/x-shellscript\r\nMime-Version: 1.0\r\n\r\nbaz\r\n--MIMEBOUNDARY\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/x-shellscript\r\nMime-Version: 1.0\r\n\r\nffbaz\r\n--MIMEBOUNDARY--\r\n", 74 | }, 75 | { 76 | "no gzip or b64 - with boundary separator", 77 | `data "cloudinit_config" "foo" { 78 | gzip = false 79 | base64_encode = false 80 | boundary = "//" 81 | 82 | part { 83 | content_type = "text/x-shellscript" 84 | content = "baz" 85 | } 86 | }`, 87 | "Content-Type: multipart/mixed; boundary=\"//\"\nMIME-Version: 1.0\r\n\r\n--//\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/x-shellscript\r\nMime-Version: 1.0\r\n\r\nbaz\r\n--//--\r\n", 88 | }, 89 | { 90 | "no gzip or b64 - two parts - all fields", 91 | `data "cloudinit_config" "foo" { 92 | gzip = false 93 | base64_encode = false 94 | 95 | part { 96 | content_type = "text/x-shellscript" 97 | content = "foo1" 98 | filename = "foofile1.txt" 99 | merge_type = "list()+dict()+str()" 100 | } 101 | 102 | part { 103 | content_type = "text/x-shellscript" 104 | content = "bar1" 105 | filename = "barfile1.txt" 106 | merge_type = "list()+dict()+str()" 107 | } 108 | }`, 109 | "Content-Type: multipart/mixed; boundary=\"MIMEBOUNDARY\"\nMIME-Version: 1.0\r\n\r\n--MIMEBOUNDARY\r\nContent-Disposition: attachment; filename=\"foofile1.txt\"\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/x-shellscript\r\nMime-Version: 1.0\r\nX-Merge-Type: list()+dict()+str()\r\n\r\nfoo1\r\n--MIMEBOUNDARY\r\nContent-Disposition: attachment; filename=\"barfile1.txt\"\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/x-shellscript\r\nMime-Version: 1.0\r\nX-Merge-Type: list()+dict()+str()\r\n\r\nbar1\r\n--MIMEBOUNDARY--\r\n", 110 | }, 111 | { 112 | "no gzip - b64 encoded - basic content", 113 | `data "cloudinit_config" "foo" { 114 | gzip = false 115 | base64_encode = true 116 | 117 | part { 118 | content_type = "text/x-shellscript" 119 | content = "heythere" 120 | } 121 | }`, 122 | "Q29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PSJNSU1FQk9VTkRBUlkiCk1JTUUtVmVyc2lvbjogMS4wDQoNCi0tTUlNRUJPVU5EQVJZDQpDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiA3Yml0DQpDb250ZW50LVR5cGU6IHRleHQveC1zaGVsbHNjcmlwdA0KTWltZS1WZXJzaW9uOiAxLjANCg0KaGV5dGhlcmUNCi0tTUlNRUJPVU5EQVJZLS0NCg==", 123 | }, 124 | { 125 | "gzip compression - basic content", 126 | `data "cloudinit_config" "foo" { 127 | gzip = true 128 | 129 | part { 130 | content_type = "text/x-shellscript" 131 | content = "heythere" 132 | } 133 | }`, 134 | "H4sIAAAAAAAA/2TNuwrCQBCF4X5h32FJP0YrIWLhJYVFFEQFy1xGM5DMhtkJJG8vWkjQ8sDP+XaeFVnhMnaYuLZvlLpcNG5pwGrlCt9zlcu4jrJDlm5P1+N+c75H5r3ghhLIc+IWs7k11gBMI2u+35JzeKBAyqWviJ+JWxakk+CDKw4aDxBqbJpQCnVqTUYt/jk1jlqj4K8IYM0rAAD//0u6BO3QAAAA", 135 | }, 136 | // Initial fix of panic when part block wasn't parsable 137 | // https://github.com/hashicorp/terraform/issues/13572 138 | // 139 | // Move to framework fixed parsing error, but introduced incorrect validation, empty content is valid 140 | // https://github.com/hashicorp/terraform-provider-cloudinit/issues/104 141 | { 142 | "empty content field in part block", 143 | `data "cloudinit_config" "foo" { 144 | part { 145 | content = "" 146 | } 147 | }`, 148 | "H4sIAAAAAAAA/3LOzytJzSvRDaksSLVSyC3NKcksSCwq0c/NrEhNsVZIyi/NS0ksqrRV8vX0dXXyD/VzcQyKVOIC8XTDUouKM/PzrBQM9Qx4uXi5dHWRFfFywc0uSswrTkst0nXNS85PycxLt1IwT8osQVIAtrwktaJEvyAnMTOPl8s3MzcVw3x0G3R1ebkAAQAA///dakG4wAAAAA==", 149 | }, 150 | { 151 | "empty content field in one part block, multiple part blocks", 152 | `data "cloudinit_config" "foo" { 153 | gzip = false 154 | base64_encode = false 155 | 156 | part { 157 | content_type = "text/cloud-config" 158 | content = "abc" 159 | } 160 | 161 | part { 162 | content = "" 163 | } 164 | 165 | part { 166 | content_type = "text/cloud-config" 167 | content = "" 168 | } 169 | }`, 170 | "Content-Type: multipart/mixed; boundary=\"MIMEBOUNDARY\"\nMIME-Version: 1.0\r\n\r\n--MIMEBOUNDARY\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/cloud-config\r\nMime-Version: 1.0\r\n\r\nabc\r\n--MIMEBOUNDARY\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/plain\r\nMime-Version: 1.0\r\n\r\n\r\n--MIMEBOUNDARY\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/cloud-config\r\nMime-Version: 1.0\r\n\r\n\r\n--MIMEBOUNDARY--\r\n", 171 | }, 172 | } 173 | 174 | for _, tt := range testCases { 175 | t.Run(tt.Name, func(t *testing.T) { 176 | r.UnitTest(t, r.TestCase{ 177 | ProtoV5ProviderFactories: testProtoV5ProviderFactories, 178 | Steps: []r.TestStep{ 179 | { 180 | Config: tt.DataSourceBlock, 181 | Check: r.ComposeTestCheckFunc( 182 | r.TestCheckResourceAttr("data.cloudinit_config.foo", "rendered", tt.Expected), 183 | ), 184 | }, 185 | }, 186 | }) 187 | }) 188 | } 189 | } 190 | 191 | func TestConfigDataSourceRender_handleErrors(t *testing.T) { 192 | testCases := []struct { 193 | Name string 194 | DataSourceBlock string 195 | ErrorMatch *regexp.Regexp 196 | }{ 197 | { 198 | "base64 can't be false when gzip is true", 199 | `data "cloudinit_config" "foo" { 200 | gzip = true 201 | base64_encode = false 202 | 203 | part { 204 | content = "abc" 205 | } 206 | }`, 207 | regexp.MustCompile("Expected base64_encode to be set to true when gzip is true"), 208 | }, 209 | { 210 | "at least one part is required", 211 | `data "cloudinit_config" "foo" { 212 | gzip = false 213 | base64_encode = false 214 | }`, 215 | regexp.MustCompile("part must have a configuration value"), 216 | }, 217 | } 218 | 219 | for _, tt := range testCases { 220 | t.Run(tt.Name, func(t *testing.T) { 221 | r.UnitTest(t, r.TestCase{ 222 | ProtoV5ProviderFactories: testProtoV5ProviderFactories, 223 | Steps: []r.TestStep{ 224 | { 225 | Config: tt.DataSourceBlock, 226 | ExpectError: tt.ErrorMatch, 227 | }, 228 | }, 229 | }) 230 | }) 231 | } 232 | } 233 | 234 | func TestConfigDataSource_UpgradeFromVersion2_2_0(t *testing.T) { 235 | testCases := []struct { 236 | Name string 237 | DataSourceBlock string 238 | Expected string 239 | }{ 240 | { 241 | "multiple parts in cloudinit config", 242 | `data "cloudinit_config" "foo" { 243 | gzip = false 244 | base64_encode = false 245 | 246 | part { 247 | content_type = "text/x-shellscript" 248 | content = "foo1" 249 | filename = "foofile1.txt" 250 | merge_type = "list()+dict()+str()" 251 | } 252 | 253 | part { 254 | content = "bar1" 255 | filename = "barfile1.txt" 256 | merge_type = "list()+dict()+str()" 257 | } 258 | }`, 259 | "Content-Type: multipart/mixed; boundary=\"MIMEBOUNDARY\"\nMIME-Version: 1.0\r\n\r\n--MIMEBOUNDARY\r\nContent-Disposition: attachment; filename=\"foofile1.txt\"\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/x-shellscript\r\nMime-Version: 1.0\r\nX-Merge-Type: list()+dict()+str()\r\n\r\nfoo1\r\n--MIMEBOUNDARY\r\nContent-Disposition: attachment; filename=\"barfile1.txt\"\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/plain\r\nMime-Version: 1.0\r\nX-Merge-Type: list()+dict()+str()\r\n\r\nbar1\r\n--MIMEBOUNDARY--\r\n", 260 | }, 261 | } 262 | 263 | for _, tt := range testCases { 264 | t.Run(tt.Name, func(t *testing.T) { 265 | r.UnitTest(t, r.TestCase{ 266 | Steps: []r.TestStep{ 267 | { 268 | ExternalProviders: map[string]r.ExternalProvider{ 269 | "cloudinit": { 270 | VersionConstraint: "2.2.0", 271 | Source: "hashicorp/cloudinit", 272 | }, 273 | }, 274 | Config: tt.DataSourceBlock, 275 | Check: r.ComposeTestCheckFunc( 276 | r.TestCheckResourceAttr("data.cloudinit_config.foo", "rendered", tt.Expected), 277 | ), 278 | }, 279 | { 280 | ProtoV5ProviderFactories: testProtoV5ProviderFactories, 281 | Config: tt.DataSourceBlock, 282 | Check: r.ComposeTestCheckFunc( 283 | r.TestCheckResourceAttr("data.cloudinit_config.foo", "rendered", tt.Expected), 284 | ), 285 | }, 286 | }, 287 | }) 288 | }) 289 | } 290 | } 291 | 292 | // This test ensures that unknown values are being handled properly in the `part` block 293 | // https://github.com/hashicorp/terraform-provider-cloudinit/issues/102 294 | func TestConfigDataSource_HandleUnknown(t *testing.T) { 295 | testCases := []struct { 296 | Name string 297 | DataSourceBlock string 298 | Expected string 299 | }{ 300 | { 301 | "issue 102 - handle unknown in validate caused by variable", 302 | ` 303 | variable "config_types" { 304 | default = ["text/cloud-config", "text/cloud-config"] 305 | } 306 | 307 | data "cloudinit_config" "foo" { 308 | gzip = true 309 | base64_encode = true 310 | 311 | dynamic "part" { 312 | for_each = var.config_types 313 | content { 314 | content_type = part.value 315 | content = <<-EOT 316 | #cloud-config 317 | test: ${part.value} 318 | EOT 319 | } 320 | } 321 | } 322 | `, 323 | "H4sIAAAAAAAA/3LOzytJzSvRDaksSLVSyC3NKcksSCwq0c/NrEhNsVZIyi/NS0ksqrRV8vX0dXXyD/VzcQyKVOIC8XTDUouKM/PzrBQM9Qx4uXi5dHWRFfFywc0uSswrTkst0nXNS85PycxLt1IwT8osQVIAtrwktaJEPzknvzRFNzk/Ly0znZfLNzM3FcMaZWQ1XCWpxSVY9A525+jq8nIBAgAA//821u5zfAEAAA==", 324 | }, 325 | } 326 | 327 | for _, tt := range testCases { 328 | t.Run(tt.Name, func(t *testing.T) { 329 | r.UnitTest(t, r.TestCase{ 330 | Steps: []r.TestStep{ 331 | { 332 | ProtoV5ProviderFactories: testProtoV5ProviderFactories, 333 | Config: tt.DataSourceBlock, 334 | Check: r.ComposeTestCheckFunc( 335 | r.TestCheckResourceAttr("data.cloudinit_config.foo", "rendered", tt.Expected), 336 | ), 337 | }, 338 | }, 339 | }) 340 | }) 341 | } 342 | } 343 | -------------------------------------------------------------------------------- /internal/provider/provider.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package provider 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/hashicorp/terraform-plugin-framework/datasource" 10 | "github.com/hashicorp/terraform-plugin-framework/provider" 11 | "github.com/hashicorp/terraform-plugin-framework/provider/schema" 12 | "github.com/hashicorp/terraform-plugin-framework/resource" 13 | ) 14 | 15 | var ( 16 | _ provider.Provider = (*cloudinitProvider)(nil) 17 | ) 18 | 19 | type cloudinitProvider struct{} 20 | 21 | func New() provider.Provider { 22 | return &cloudinitProvider{} 23 | } 24 | 25 | func (p *cloudinitProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) { 26 | resp.TypeName = "cloudinit" 27 | } 28 | 29 | func (p *cloudinitProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { 30 | resp.Schema = schema.Schema{ 31 | MarkdownDescription: "The cloud-init Terraform provider exposes the `cloudinit_config` data source, previously available as the " + 32 | "`template_cloudinit_config` resource [in the template provider](https://registry.terraform.io/providers/hashicorp/template/latest/docs/data-sources/cloudinit_config), " + 33 | "which renders a [multipart MIME configuration](https://cloudinit.readthedocs.io/en/latest/explanation/format.html#mime-multi-part-archive) " + 34 | "for use with [cloud-init](https://cloudinit.readthedocs.io/en/latest/).", 35 | } 36 | } 37 | 38 | func (p *cloudinitProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { 39 | } 40 | 41 | func (p *cloudinitProvider) Resources(ctx context.Context) []func() resource.Resource { 42 | return []func() resource.Resource{ 43 | func() resource.Resource { 44 | return &configResource{} 45 | }, 46 | } 47 | } 48 | 49 | func (p *cloudinitProvider) DataSources(ctx context.Context) []func() datasource.DataSource { 50 | return []func() datasource.DataSource{ 51 | func() datasource.DataSource { 52 | return &configDataSource{} 53 | }, 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /internal/provider/provider_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package provider 5 | 6 | import ( 7 | "github.com/hashicorp/terraform-plugin-framework/providerserver" 8 | "github.com/hashicorp/terraform-plugin-go/tfprotov5" 9 | ) 10 | 11 | var testProtoV5ProviderFactories = map[string]func() (tfprotov5.ProviderServer, error){ 12 | "cloudinit": providerserver.NewProtocol5WithError(New()), 13 | } 14 | -------------------------------------------------------------------------------- /internal/provider/resource_cloudinit_config.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package provider 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" 10 | "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" 11 | "github.com/hashicorp/terraform-plugin-framework/resource" 12 | "github.com/hashicorp/terraform-plugin-framework/resource/schema" 13 | "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" 14 | "github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier" 15 | "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" 16 | "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" 17 | "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" 18 | "github.com/hashicorp/terraform-plugin-framework/schema/validator" 19 | ) 20 | 21 | var ( 22 | _ resource.ResourceWithValidateConfig = (*configResource)(nil) 23 | ) 24 | 25 | type configResource struct{} 26 | 27 | func (r *configResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { 28 | resp.TypeName = req.ProviderTypeName + "_config" 29 | } 30 | 31 | func (r *configResource) ValidateConfig(ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse) { 32 | var cloudinitConfig configModel 33 | 34 | resp.Diagnostics.Append(req.Config.Get(ctx, &cloudinitConfig)...) 35 | if resp.Diagnostics.HasError() { 36 | return 37 | } 38 | 39 | resp.Diagnostics.Append(cloudinitConfig.validate(ctx)...) 40 | } 41 | 42 | func (r *configResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { 43 | resp.Schema = schema.Schema{ 44 | Blocks: map[string]schema.Block{ 45 | "part": schema.ListNestedBlock{ 46 | Validators: []validator.List{ 47 | listvalidator.IsRequired(), 48 | }, 49 | NestedObject: schema.NestedBlockObject{ 50 | Attributes: map[string]schema.Attribute{ 51 | "content_type": schema.StringAttribute{ 52 | Validators: []validator.String{ 53 | stringvalidator.LengthAtLeast(1), 54 | }, 55 | PlanModifiers: []planmodifier.String{ 56 | stringplanmodifier.RequiresReplace(), 57 | }, 58 | Optional: true, 59 | Computed: true, 60 | Default: stringdefault.StaticString("text/plain"), 61 | MarkdownDescription: "A MIME-style content type to report in the header for the part. Defaults to `text/plain`", 62 | }, 63 | "content": schema.StringAttribute{ 64 | PlanModifiers: []planmodifier.String{ 65 | stringplanmodifier.RequiresReplace(), 66 | }, 67 | Required: true, 68 | MarkdownDescription: "Body content for the part.", 69 | }, 70 | "filename": schema.StringAttribute{ 71 | PlanModifiers: []planmodifier.String{ 72 | stringplanmodifier.RequiresReplace(), 73 | }, 74 | Optional: true, 75 | MarkdownDescription: "A filename to report in the header for the part.", 76 | }, 77 | "merge_type": schema.StringAttribute{ 78 | PlanModifiers: []planmodifier.String{ 79 | stringplanmodifier.RequiresReplace(), 80 | }, 81 | Optional: true, 82 | MarkdownDescription: "A value for the `X-Merge-Type` header of the part, to control " + 83 | "[cloud-init merging behavior](https://cloudinit.readthedocs.io/en/latest/reference/merging.html).", 84 | }, 85 | }, 86 | }, 87 | MarkdownDescription: "A nested block type which adds a file to the generated cloud-init configuration. Use multiple " + 88 | "`part` blocks to specify multiple files, which will be included in order of declaration in the final MIME document.", 89 | }, 90 | }, 91 | Attributes: map[string]schema.Attribute{ 92 | "gzip": schema.BoolAttribute{ 93 | PlanModifiers: []planmodifier.Bool{ 94 | boolplanmodifier.RequiresReplace(), 95 | }, 96 | Optional: true, 97 | Computed: true, 98 | Default: booldefault.StaticBool(true), 99 | MarkdownDescription: "Specify whether or not to gzip the `rendered` output. Defaults to `true`.", 100 | }, 101 | "base64_encode": schema.BoolAttribute{ 102 | PlanModifiers: []planmodifier.Bool{ 103 | boolplanmodifier.RequiresReplace(), 104 | }, 105 | Optional: true, 106 | Computed: true, 107 | Default: booldefault.StaticBool(true), 108 | MarkdownDescription: "Specify whether or not to base64 encode the `rendered` output. Defaults to `true`, and cannot be disabled if gzip is `true`.", 109 | }, 110 | "boundary": schema.StringAttribute{ 111 | Validators: []validator.String{ 112 | stringvalidator.LengthAtLeast(1), 113 | }, 114 | PlanModifiers: []planmodifier.String{ 115 | stringplanmodifier.RequiresReplace(), 116 | }, 117 | Optional: true, 118 | Computed: true, 119 | Default: stringdefault.StaticString("MIMEBOUNDARY"), 120 | MarkdownDescription: "Specify the Writer's default boundary separator. Defaults to `MIMEBOUNDARY`.", 121 | }, 122 | "rendered": schema.StringAttribute{ 123 | Computed: true, 124 | MarkdownDescription: "The final rendered multi-part cloud-init config.", 125 | }, 126 | "id": schema.StringAttribute{ 127 | Computed: true, 128 | MarkdownDescription: "[CRC-32](https://pkg.go.dev/hash/crc32) checksum of `rendered` cloud-init config.", 129 | }, 130 | }, 131 | DeprecationMessage: `This resource is deprecated, please use the cloudinit_config data source instead.`, 132 | MarkdownDescription: "Renders a [multi-part MIME configuration](https://cloudinit.readthedocs.io/en/latest/explanation/format.html#mime-multi-part-archive) " + 133 | "for use with [cloud-init](https://cloudinit.readthedocs.io/en/latest/).\n\n" + 134 | "Cloud-init is a commonly-used startup configuration utility for cloud compute instances. It accepts configuration via provider-specific " + 135 | "user data mechanisms, such as `user_data` for Amazon EC2 instances. Multi-part MIME is one of the data formats it accepts. For more information, " + 136 | "see [User-Data Formats](https://cloudinit.readthedocs.io/en/latest/explanation/format.html) in the cloud-init manual.\n\n" + 137 | "This is not a generalized utility for producing multi-part MIME messages. Its feature set is specialized for cloud-init multi-part MIME messages.", 138 | } 139 | } 140 | 141 | func (r *configResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { 142 | var cloudinitConfig configModel 143 | 144 | resp.Diagnostics.Append(req.Plan.Get(ctx, &cloudinitConfig)...) 145 | if resp.Diagnostics.HasError() { 146 | return 147 | } 148 | 149 | resp.Diagnostics.Append(cloudinitConfig.update(ctx)...) 150 | resp.Diagnostics.Append(resp.State.Set(ctx, cloudinitConfig)...) 151 | } 152 | 153 | func (r *configResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { 154 | } 155 | 156 | func (r *configResource) Update(_ context.Context, _ resource.UpdateRequest, _ *resource.UpdateResponse) { 157 | } 158 | 159 | func (r *configResource) Delete(_ context.Context, _ resource.DeleteRequest, _ *resource.DeleteResponse) { 160 | } 161 | -------------------------------------------------------------------------------- /internal/provider/resource_cloudinit_config_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package provider 5 | 6 | import ( 7 | "regexp" 8 | "testing" 9 | 10 | r "github.com/hashicorp/terraform-plugin-testing/helper/resource" 11 | ) 12 | 13 | func TestConfigResourceRender(t *testing.T) { 14 | testCases := []struct { 15 | Name string 16 | ResourceBlock string 17 | Expected string 18 | }{ 19 | { 20 | "no gzip or b64 - basic content", 21 | `resource "cloudinit_config" "foo" { 22 | gzip = false 23 | base64_encode = false 24 | 25 | part { 26 | content_type = "text/x-shellscript" 27 | content = "baz" 28 | } 29 | }`, 30 | "Content-Type: multipart/mixed; boundary=\"MIMEBOUNDARY\"\nMIME-Version: 1.0\r\n\r\n--MIMEBOUNDARY\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/x-shellscript\r\nMime-Version: 1.0\r\n\r\nbaz\r\n--MIMEBOUNDARY--\r\n", 31 | }, 32 | { 33 | "no gzip or b64 - basic content - default to text plain", 34 | `resource "cloudinit_config" "foo" { 35 | gzip = false 36 | base64_encode = false 37 | 38 | part { 39 | content = "baz" 40 | } 41 | }`, 42 | "Content-Type: multipart/mixed; boundary=\"MIMEBOUNDARY\"\nMIME-Version: 1.0\r\n\r\n--MIMEBOUNDARY\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/plain\r\nMime-Version: 1.0\r\n\r\nbaz\r\n--MIMEBOUNDARY--\r\n", 43 | }, 44 | { 45 | "no gzip or b64 - content with filename", 46 | `resource "cloudinit_config" "foo" { 47 | gzip = false 48 | base64_encode = false 49 | 50 | part { 51 | content_type = "text/x-shellscript" 52 | content = "baz" 53 | filename = "foobar.sh" 54 | } 55 | }`, 56 | "Content-Type: multipart/mixed; boundary=\"MIMEBOUNDARY\"\nMIME-Version: 1.0\r\n\r\n--MIMEBOUNDARY\r\nContent-Disposition: attachment; filename=\"foobar.sh\"\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/x-shellscript\r\nMime-Version: 1.0\r\n\r\nbaz\r\n--MIMEBOUNDARY--\r\n", 57 | }, 58 | { 59 | "no gzip or b64 - two parts, basic content", 60 | `resource "cloudinit_config" "foo" { 61 | gzip = false 62 | base64_encode = false 63 | 64 | part { 65 | content_type = "text/x-shellscript" 66 | content = "baz" 67 | } 68 | part { 69 | content_type = "text/x-shellscript" 70 | content = "ffbaz" 71 | } 72 | }`, 73 | "Content-Type: multipart/mixed; boundary=\"MIMEBOUNDARY\"\nMIME-Version: 1.0\r\n\r\n--MIMEBOUNDARY\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/x-shellscript\r\nMime-Version: 1.0\r\n\r\nbaz\r\n--MIMEBOUNDARY\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/x-shellscript\r\nMime-Version: 1.0\r\n\r\nffbaz\r\n--MIMEBOUNDARY--\r\n", 74 | }, 75 | { 76 | "no gzip or b64 - with boundary separator", 77 | `resource "cloudinit_config" "foo" { 78 | gzip = false 79 | base64_encode = false 80 | boundary = "//" 81 | 82 | part { 83 | content_type = "text/x-shellscript" 84 | content = "baz" 85 | } 86 | }`, 87 | "Content-Type: multipart/mixed; boundary=\"//\"\nMIME-Version: 1.0\r\n\r\n--//\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/x-shellscript\r\nMime-Version: 1.0\r\n\r\nbaz\r\n--//--\r\n", 88 | }, 89 | { 90 | "no gzip or b64 - two parts - all fields", 91 | `resource "cloudinit_config" "foo" { 92 | gzip = false 93 | base64_encode = false 94 | 95 | part { 96 | content_type = "text/x-shellscript" 97 | content = "foo1" 98 | filename = "foofile1.txt" 99 | merge_type = "list()+dict()+str()" 100 | } 101 | 102 | part { 103 | content_type = "text/x-shellscript" 104 | content = "bar1" 105 | filename = "barfile1.txt" 106 | merge_type = "list()+dict()+str()" 107 | } 108 | }`, 109 | "Content-Type: multipart/mixed; boundary=\"MIMEBOUNDARY\"\nMIME-Version: 1.0\r\n\r\n--MIMEBOUNDARY\r\nContent-Disposition: attachment; filename=\"foofile1.txt\"\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/x-shellscript\r\nMime-Version: 1.0\r\nX-Merge-Type: list()+dict()+str()\r\n\r\nfoo1\r\n--MIMEBOUNDARY\r\nContent-Disposition: attachment; filename=\"barfile1.txt\"\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/x-shellscript\r\nMime-Version: 1.0\r\nX-Merge-Type: list()+dict()+str()\r\n\r\nbar1\r\n--MIMEBOUNDARY--\r\n", 110 | }, 111 | { 112 | "no gzip - b64 encoded - basic content", 113 | `resource "cloudinit_config" "foo" { 114 | gzip = false 115 | base64_encode = true 116 | 117 | part { 118 | content_type = "text/x-shellscript" 119 | content = "heythere" 120 | } 121 | }`, 122 | "Q29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PSJNSU1FQk9VTkRBUlkiCk1JTUUtVmVyc2lvbjogMS4wDQoNCi0tTUlNRUJPVU5EQVJZDQpDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiA3Yml0DQpDb250ZW50LVR5cGU6IHRleHQveC1zaGVsbHNjcmlwdA0KTWltZS1WZXJzaW9uOiAxLjANCg0KaGV5dGhlcmUNCi0tTUlNRUJPVU5EQVJZLS0NCg==", 123 | }, 124 | { 125 | "gzip compression - basic content", 126 | `resource "cloudinit_config" "foo" { 127 | gzip = true 128 | 129 | part { 130 | content_type = "text/x-shellscript" 131 | content = "heythere" 132 | } 133 | }`, 134 | "H4sIAAAAAAAA/2TNuwrCQBCF4X5h32FJP0YrIWLhJYVFFEQFy1xGM5DMhtkJJG8vWkjQ8sDP+XaeFVnhMnaYuLZvlLpcNG5pwGrlCt9zlcu4jrJDlm5P1+N+c75H5r3ghhLIc+IWs7k11gBMI2u+35JzeKBAyqWviJ+JWxakk+CDKw4aDxBqbJpQCnVqTUYt/jk1jlqj4K8IYM0rAAD//0u6BO3QAAAA", 135 | }, 136 | // Initial fix of panic when part block wasn't parsable 137 | // https://github.com/hashicorp/terraform/issues/13572 138 | // 139 | // Move to framework fixed parsing error, but introduced incorrect validation, empty content is valid 140 | // https://github.com/hashicorp/terraform-provider-cloudinit/issues/104 141 | { 142 | "empty content field in part block", 143 | `resource "cloudinit_config" "foo" { 144 | part { 145 | content = "" 146 | } 147 | }`, 148 | "H4sIAAAAAAAA/3LOzytJzSvRDaksSLVSyC3NKcksSCwq0c/NrEhNsVZIyi/NS0ksqrRV8vX0dXXyD/VzcQyKVOIC8XTDUouKM/PzrBQM9Qx4uXi5dHWRFfFywc0uSswrTkst0nXNS85PycxLt1IwT8osQVIAtrwktaJEvyAnMTOPl8s3MzcVw3x0G3R1ebkAAQAA///dakG4wAAAAA==", 149 | }, 150 | { 151 | "empty content field in one part block, multiple part blocks", 152 | `resource "cloudinit_config" "foo" { 153 | gzip = false 154 | base64_encode = false 155 | 156 | part { 157 | content_type = "text/cloud-config" 158 | content = "abc" 159 | } 160 | 161 | part { 162 | content = "" 163 | } 164 | 165 | part { 166 | content_type = "text/cloud-config" 167 | content = "" 168 | } 169 | }`, 170 | "Content-Type: multipart/mixed; boundary=\"MIMEBOUNDARY\"\nMIME-Version: 1.0\r\n\r\n--MIMEBOUNDARY\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/cloud-config\r\nMime-Version: 1.0\r\n\r\nabc\r\n--MIMEBOUNDARY\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/plain\r\nMime-Version: 1.0\r\n\r\n\r\n--MIMEBOUNDARY\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/cloud-config\r\nMime-Version: 1.0\r\n\r\n\r\n--MIMEBOUNDARY--\r\n", 171 | }, 172 | } 173 | 174 | for _, tt := range testCases { 175 | t.Run(tt.Name, func(t *testing.T) { 176 | r.UnitTest(t, r.TestCase{ 177 | ProtoV5ProviderFactories: testProtoV5ProviderFactories, 178 | Steps: []r.TestStep{ 179 | { 180 | Config: tt.ResourceBlock, 181 | Check: r.ComposeTestCheckFunc( 182 | r.TestCheckResourceAttr("cloudinit_config.foo", "rendered", tt.Expected), 183 | ), 184 | }, 185 | }, 186 | }) 187 | }) 188 | } 189 | } 190 | 191 | func TestConfigResourceRender_handleErrors(t *testing.T) { 192 | testCases := []struct { 193 | Name string 194 | ResourceBlock string 195 | ErrorMatch *regexp.Regexp 196 | }{ 197 | { 198 | "base64 can't be false when gzip is true", 199 | `resource "cloudinit_config" "foo" { 200 | gzip = true 201 | base64_encode = false 202 | 203 | part { 204 | content = "abc" 205 | } 206 | }`, 207 | regexp.MustCompile("Expected base64_encode to be set to true when gzip is true"), 208 | }, 209 | { 210 | "at least one part is required", 211 | `resource "cloudinit_config" "foo" { 212 | gzip = false 213 | base64_encode = false 214 | }`, 215 | regexp.MustCompile("part must have a configuration value"), 216 | }, 217 | } 218 | 219 | for _, tt := range testCases { 220 | t.Run(tt.Name, func(t *testing.T) { 221 | r.UnitTest(t, r.TestCase{ 222 | ProtoV5ProviderFactories: testProtoV5ProviderFactories, 223 | Steps: []r.TestStep{ 224 | { 225 | Config: tt.ResourceBlock, 226 | ExpectError: tt.ErrorMatch, 227 | }, 228 | }, 229 | }) 230 | }) 231 | } 232 | } 233 | 234 | func TestConfigResource_UpgradeFromVersion2_2_0(t *testing.T) { 235 | testCases := []struct { 236 | Name string 237 | ResourceBlock string 238 | Expected string 239 | }{ 240 | { 241 | "multiple parts in cloudinit config", 242 | `resource "cloudinit_config" "foo" { 243 | gzip = false 244 | base64_encode = false 245 | 246 | part { 247 | content_type = "text/x-shellscript" 248 | content = "foo1" 249 | filename = "foofile1.txt" 250 | merge_type = "list()+dict()+str()" 251 | } 252 | 253 | part { 254 | content = "bar1" 255 | filename = "barfile1.txt" 256 | merge_type = "list()+dict()+str()" 257 | } 258 | }`, 259 | "Content-Type: multipart/mixed; boundary=\"MIMEBOUNDARY\"\nMIME-Version: 1.0\r\n\r\n--MIMEBOUNDARY\r\nContent-Disposition: attachment; filename=\"foofile1.txt\"\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/x-shellscript\r\nMime-Version: 1.0\r\nX-Merge-Type: list()+dict()+str()\r\n\r\nfoo1\r\n--MIMEBOUNDARY\r\nContent-Disposition: attachment; filename=\"barfile1.txt\"\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/plain\r\nMime-Version: 1.0\r\nX-Merge-Type: list()+dict()+str()\r\n\r\nbar1\r\n--MIMEBOUNDARY--\r\n", 260 | }, 261 | } 262 | 263 | for _, tt := range testCases { 264 | t.Run(tt.Name, func(t *testing.T) { 265 | r.UnitTest(t, r.TestCase{ 266 | Steps: []r.TestStep{ 267 | { 268 | ExternalProviders: map[string]r.ExternalProvider{ 269 | "cloudinit": { 270 | VersionConstraint: "2.2.0", 271 | Source: "hashicorp/cloudinit", 272 | }, 273 | }, 274 | Config: tt.ResourceBlock, 275 | Check: r.ComposeTestCheckFunc( 276 | r.TestCheckResourceAttr("cloudinit_config.foo", "rendered", tt.Expected), 277 | ), 278 | }, 279 | { 280 | ProtoV5ProviderFactories: testProtoV5ProviderFactories, 281 | Config: tt.ResourceBlock, 282 | Check: r.ComposeTestCheckFunc( 283 | r.TestCheckResourceAttr("cloudinit_config.foo", "rendered", tt.Expected), 284 | ), 285 | }, 286 | }, 287 | }) 288 | }) 289 | } 290 | } 291 | 292 | // This test ensures that unknown values are being handled properly in the `part` block 293 | // https://github.com/hashicorp/terraform-provider-cloudinit/issues/102 294 | func TestConfigResource_HandleUnknown(t *testing.T) { 295 | testCases := []struct { 296 | Name string 297 | ResourceBlock string 298 | Expected string 299 | }{ 300 | { 301 | "issue 102 - handle unknown in validate caused by variable", 302 | ` 303 | variable "config_types" { 304 | default = ["text/cloud-config", "text/cloud-config"] 305 | } 306 | 307 | resource "cloudinit_config" "foo" { 308 | gzip = true 309 | base64_encode = true 310 | 311 | dynamic "part" { 312 | for_each = var.config_types 313 | content { 314 | content_type = part.value 315 | content = <<-EOT 316 | #cloud-config 317 | test: ${part.value} 318 | EOT 319 | } 320 | } 321 | } 322 | `, 323 | "H4sIAAAAAAAA/3LOzytJzSvRDaksSLVSyC3NKcksSCwq0c/NrEhNsVZIyi/NS0ksqrRV8vX0dXXyD/VzcQyKVOIC8XTDUouKM/PzrBQM9Qx4uXi5dHWRFfFywc0uSswrTkst0nXNS85PycxLt1IwT8osQVIAtrwktaJEPzknvzRFNzk/Ly0znZfLNzM3FcMaZWQ1XCWpxSVY9A525+jq8nIBAgAA//821u5zfAEAAA==", 324 | }, 325 | } 326 | 327 | for _, tt := range testCases { 328 | t.Run(tt.Name, func(t *testing.T) { 329 | r.UnitTest(t, r.TestCase{ 330 | Steps: []r.TestStep{ 331 | { 332 | ProtoV5ProviderFactories: testProtoV5ProviderFactories, 333 | Config: tt.ResourceBlock, 334 | Check: r.ComposeTestCheckFunc( 335 | r.TestCheckResourceAttr("cloudinit_config.foo", "rendered", tt.Expected), 336 | ), 337 | }, 338 | }, 339 | }) 340 | }) 341 | } 342 | } 343 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package main 5 | 6 | import ( 7 | "context" 8 | "flag" 9 | "log" 10 | 11 | "github.com/hashicorp/terraform-plugin-framework/providerserver" 12 | 13 | "github.com/hashicorp/terraform-provider-cloudinit/internal/provider" 14 | ) 15 | 16 | func main() { 17 | var debug bool 18 | 19 | flag.BoolVar(&debug, "debug", false, "set to true to run the provider with support for debuggers like delve") 20 | flag.Parse() 21 | 22 | err := providerserver.Serve(context.Background(), provider.New, providerserver.ServeOpts{ 23 | Address: "registry.terraform.io/hashicorp/cloudinit", 24 | Debug: debug, 25 | ProtocolVersion: 5, 26 | }) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /templates/data-sources/config.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" 3 | description: |- 4 | {{ .Description | plainmarkdown | trimspace | prefixlines " " }} 5 | --- 6 | 7 | # {{.Name}} ({{.Type}}) 8 | 9 | {{ .Description }} 10 | 11 | ## Example Usage 12 | 13 | ### Config 14 | {{ tffile "examples/data-sources/cloudinit_config/data-source.tf" }} 15 | 16 | ### hello-script.sh 17 | {{ codefile "shell" "examples/data-sources/cloudinit_config/hello-script.sh" }} 18 | 19 | ### cloud-config.yaml 20 | {{ codefile "yaml" "examples/data-sources/cloudinit_config/cloud-config.yaml" }} 21 | 22 | 23 | 24 | ## Schema 25 | 26 | ### Required 27 | 28 | - `part` (Block List) A nested block type which adds a file to the generated cloud-init configuration. Use multiple `part` blocks to specify multiple files, which will be included in order of declaration in the final MIME document. (see [below for nested schema](#nestedblock--part)) 29 | 30 | ### Optional 31 | 32 | - `base64_encode` (Boolean) Specify whether or not to base64 encode the `rendered` output. Defaults to `true`, and cannot be disabled if gzip is `true`. 33 | - `boundary` (String) Specify the Writer's default boundary separator. Defaults to `MIMEBOUNDARY`. 34 | - `gzip` (Boolean) Specify whether or not to gzip the `rendered` output. Defaults to `true`. 35 | 36 | ### Read-Only 37 | 38 | - `id` (String) [CRC-32](https://pkg.go.dev/hash/crc32) checksum of `rendered` cloud-init config. 39 | - `rendered` (String) The final rendered multi-part cloud-init config. 40 | 41 | 42 | ### Nested Schema for `part` 43 | 44 | Required: 45 | 46 | - `content` (String) Body content for the part. 47 | 48 | Optional: 49 | 50 | - `content_type` (String) A MIME-style content type to report in the header for the part. Defaults to `text/plain` 51 | - `filename` (String) A filename to report in the header for the part. 52 | - `merge_type` (String) A value for the `X-Merge-Type` header of the part, to control [cloud-init merging behavior](https://cloudinit.readthedocs.io/en/latest/reference/merging.html). 53 | -------------------------------------------------------------------------------- /templates/index.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "{{ .ProviderShortName }} Provider" 3 | description: |- 4 | {{ .Description | plainmarkdown }} 5 | --- 6 | 7 | # {{ .ProviderShortName }} Provider 8 | 9 | {{ .Description }} 10 | 11 | This provider requires no configuration. For information on the resources it provides, see the navigation bar. 12 | 13 | {{- /* No schema in this provider, so no need for this: .SchemaMarkdown | trimspace */ -}} -------------------------------------------------------------------------------- /templates/resources/config.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" 3 | description: |- 4 | {{ .Description | plainmarkdown | trimspace | prefixlines " " }} 5 | --- 6 | 7 | # {{.Name}} ({{.Type}}) 8 | 9 | ~> **This resource is deprecated** Please use the [cloudinit_config](https://registry.terraform.io/providers/hashicorp/cloudinit/latest/docs/data-sources/config) 10 | data source instead. 11 | 12 | {{ .Description }} 13 | 14 | ## Example Usage 15 | 16 | ### Config 17 | {{ tffile "examples/resources/cloudinit_config/resource.tf" }} 18 | 19 | ### hello-script.sh 20 | {{ codefile "shell" "examples/resources/cloudinit_config/hello-script.sh" }} 21 | 22 | ### cloud-config.yaml 23 | {{ codefile "yaml" "examples/resources/cloudinit_config/cloud-config.yaml" }} 24 | 25 | 26 | 27 | ## Schema 28 | 29 | ### Required 30 | 31 | - `part` (Block List) A nested block type which adds a file to the generated cloud-init configuration. Use multiple `part` blocks to specify multiple files, which will be included in order of declaration in the final MIME document. (see [below for nested schema](#nestedblock--part)) 32 | 33 | ### Optional 34 | 35 | - `base64_encode` (Boolean) Specify whether or not to base64 encode the `rendered` output. Defaults to `true`, and cannot be disabled if gzip is `true`. 36 | - `boundary` (String) Specify the Writer's default boundary separator. Defaults to `MIMEBOUNDARY`. 37 | - `gzip` (Boolean) Specify whether or not to gzip the `rendered` output. Defaults to `true`. 38 | 39 | ### Read-Only 40 | 41 | - `id` (String) [CRC-32](https://pkg.go.dev/hash/crc32) checksum of `rendered` cloud-init config. 42 | - `rendered` (String) The final rendered multi-part cloud-init config. 43 | 44 | 45 | ### Nested Schema for `part` 46 | 47 | Required: 48 | 49 | - `content` (String) Body content for the part. 50 | 51 | Optional: 52 | 53 | - `content_type` (String) A MIME-style content type to report in the header for the part. Defaults to `text/plain` 54 | - `filename` (String) A filename to report in the header for the part. 55 | - `merge_type` (String) A value for the `X-Merge-Type` header of the part, to control [cloud-init merging behavior](https://cloudinit.readthedocs.io/en/latest/reference/merging.html). 56 | -------------------------------------------------------------------------------- /terraform-registry-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "metadata": { 4 | "protocol_versions": ["5.0"] 5 | } 6 | } -------------------------------------------------------------------------------- /tools/go.mod: -------------------------------------------------------------------------------- 1 | module tools 2 | 3 | go 1.23.7 4 | 5 | require ( 6 | github.com/hashicorp/copywrite v0.22.0 7 | github.com/hashicorp/terraform-plugin-docs v0.21.0 8 | ) 9 | 10 | require ( 11 | dario.cat/mergo v1.0.1 // indirect 12 | github.com/AlecAivazis/survey/v2 v2.3.7 // indirect 13 | github.com/BurntSushi/toml v1.2.1 // indirect 14 | github.com/Kunde21/markdownfmt/v3 v3.1.0 // indirect 15 | github.com/Masterminds/goutils v1.1.1 // indirect 16 | github.com/Masterminds/semver/v3 v3.3.0 // indirect 17 | github.com/Masterminds/sprig/v3 v3.3.0 // indirect 18 | github.com/ProtonMail/go-crypto v1.1.3 // indirect 19 | github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect 20 | github.com/armon/go-radix v1.0.0 // indirect 21 | github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect 22 | github.com/bgentry/speakeasy v0.1.0 // indirect 23 | github.com/bmatcuk/doublestar/v4 v4.8.1 // indirect 24 | github.com/bradleyfalzon/ghinstallation/v2 v2.5.0 // indirect 25 | github.com/cli/go-gh/v2 v2.12.1 // indirect 26 | github.com/cli/safeexec v1.0.0 // indirect 27 | github.com/cloudflare/circl v1.6.1 // indirect 28 | github.com/fatih/color v1.16.0 // indirect 29 | github.com/fsnotify/fsnotify v1.5.4 // indirect 30 | github.com/go-openapi/errors v0.20.2 // indirect 31 | github.com/go-openapi/strfmt v0.21.3 // indirect 32 | github.com/golang-jwt/jwt/v4 v4.5.2 // indirect 33 | github.com/golang/protobuf v1.5.2 // indirect 34 | github.com/google/go-github/v45 v45.2.0 // indirect 35 | github.com/google/go-github/v53 v53.0.0 // indirect 36 | github.com/google/go-querystring v1.1.0 // indirect 37 | github.com/google/uuid v1.6.0 // indirect 38 | github.com/hashicorp/cli v1.1.7 // indirect 39 | github.com/hashicorp/errwrap v1.1.0 // indirect 40 | github.com/hashicorp/go-checkpoint v0.5.0 // indirect 41 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect 42 | github.com/hashicorp/go-hclog v1.6.3 // indirect 43 | github.com/hashicorp/go-multierror v1.1.1 // indirect 44 | github.com/hashicorp/go-retryablehttp v0.7.7 // indirect 45 | github.com/hashicorp/go-uuid v1.0.3 // indirect 46 | github.com/hashicorp/go-version v1.7.0 // indirect 47 | github.com/hashicorp/hc-install v0.9.1 // indirect 48 | github.com/hashicorp/hcl v1.0.0 // indirect 49 | github.com/hashicorp/terraform-exec v0.22.0 // indirect 50 | github.com/hashicorp/terraform-json v0.24.0 // indirect 51 | github.com/huandu/xstrings v1.5.0 // indirect 52 | github.com/inconshreveable/mousetrap v1.0.1 // indirect 53 | github.com/jedib0t/go-pretty v4.3.0+incompatible // indirect 54 | github.com/jedib0t/go-pretty/v6 v6.4.6 // indirect 55 | github.com/joho/godotenv v1.3.0 // indirect 56 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect 57 | github.com/knadh/koanf v1.5.0 // indirect 58 | github.com/mattn/go-colorable v0.1.14 // indirect 59 | github.com/mattn/go-isatty v0.0.20 // indirect 60 | github.com/mattn/go-runewidth v0.0.16 // indirect 61 | github.com/mergestat/timediff v0.0.3 // indirect 62 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect 63 | github.com/mitchellh/copystructure v1.2.0 // indirect 64 | github.com/mitchellh/go-homedir v1.1.0 // indirect 65 | github.com/mitchellh/mapstructure v1.5.0 // indirect 66 | github.com/mitchellh/reflectwalk v1.0.2 // indirect 67 | github.com/oklog/ulid v1.3.1 // indirect 68 | github.com/posener/complete v1.2.3 // indirect 69 | github.com/rivo/uniseg v0.4.7 // indirect 70 | github.com/samber/lo v1.37.0 // indirect 71 | github.com/shopspring/decimal v1.4.0 // indirect 72 | github.com/spf13/cast v1.7.0 // indirect 73 | github.com/spf13/cobra v1.6.1 // indirect 74 | github.com/spf13/pflag v1.0.5 // indirect 75 | github.com/thanhpk/randstr v1.0.4 // indirect 76 | github.com/yuin/goldmark v1.7.8 // indirect 77 | github.com/yuin/goldmark-meta v1.1.0 // indirect 78 | github.com/zclconf/go-cty v1.16.2 // indirect 79 | go.abhg.dev/goldmark/frontmatter v0.2.0 // indirect 80 | go.mongodb.org/mongo-driver v1.10.0 // indirect 81 | golang.org/x/crypto v0.36.0 // indirect 82 | golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect 83 | golang.org/x/mod v0.22.0 // indirect 84 | golang.org/x/net v0.38.0 // indirect 85 | golang.org/x/oauth2 v0.8.0 // indirect 86 | golang.org/x/sync v0.12.0 // indirect 87 | golang.org/x/sys v0.31.0 // indirect 88 | golang.org/x/term v0.30.0 // indirect 89 | golang.org/x/text v0.23.0 // indirect 90 | google.golang.org/appengine v1.6.7 // indirect 91 | google.golang.org/protobuf v1.33.0 // indirect 92 | gopkg.in/yaml.v2 v2.4.0 // indirect 93 | gopkg.in/yaml.v3 v3.0.1 // indirect 94 | ) 95 | -------------------------------------------------------------------------------- /tools/tools.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | //go:build generate 5 | 6 | package tools 7 | 8 | import ( 9 | // document generation 10 | _ "github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs" 11 | // copywrite header generation 12 | _ "github.com/hashicorp/copywrite" 13 | ) 14 | 15 | // Generate copyright headers 16 | //go:generate go run github.com/hashicorp/copywrite headers -d .. --config ../.copywrite.hcl 17 | // Format Terraform code for use in documentation. 18 | // If you do not have Terraform installed, you can remove the formatting command, but it is suggested 19 | // to ensure the documentation is formatted properly. 20 | //go:generate terraform fmt -recursive ../examples/ 21 | // Generate documentation. 22 | //go:generate go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs generate --provider-dir .. 23 | -------------------------------------------------------------------------------- /version/VERSION: -------------------------------------------------------------------------------- 1 | 2.3.7 2 | --------------------------------------------------------------------------------