├── .github ├── FUNDING.yml ├── dependabot.yml ├── release.yml └── workflows │ ├── tagpr-release.yml │ └── test.yml ├── .gitignore ├── .goreleaser.yml ├── .tagpr ├── CHANGELOG.md ├── CREDITS ├── LICENSE ├── Makefile ├── README.md ├── cmd └── tfstate-lookup │ └── main.go ├── go.mod ├── go.sum └── tfstate ├── export_test.go ├── funcs.go ├── funcs_test.go ├── jsonnet.go ├── jsonnet_test.go ├── lookup.go ├── lookup_test.go ├── remote.go ├── remote_azurerm.go ├── remote_gcs.go ├── remote_http.go ├── remote_s3.go ├── remote_s3_test.go ├── remote_tfe.go ├── roundtrip ├── LICENSE ├── README.md ├── v1-simple.in.tfstate ├── v1-simple.out.tfstate ├── v3-bigint.in.tfstate ├── v3-bigint.out.tfstate ├── v3-grabbag.in.tfstate ├── v3-grabbag.out.tfstate ├── v3-invalid-depends.in.tfstate ├── v3-invalid-depends.out.tfstate ├── v3-simple.in.tfstate ├── v3-simple.out.tfstate ├── v4-cbd.in.tfstate ├── v4-cbd.out.tfstate ├── v4-foreach.in.tfstate ├── v4-foreach.out.tfstate ├── v4-legacy-foreach.in.tfstate ├── v4-legacy-foreach.out.tfstate ├── v4-legacy-modules.in.tfstate ├── v4-legacy-modules.out.tfstate ├── v4-legacy-simple.in.tfstate ├── v4-legacy-simple.out.tfstate ├── v4-modules.in.tfstate ├── v4-modules.out.tfstate ├── v4-simple.in.tfstate └── v4-simple.out.tfstate ├── roundtrip_test.go └── test └── terraform.tfstate /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [fujiwara] 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gomod 4 | directory: "/" 5 | schedule: 6 | interval: monthly 7 | target-branch: main 8 | groups: 9 | aws-sdk-go-v2: 10 | patterns: 11 | - "github.com/aws/aws-sdk-go-v2*" 12 | - package-ecosystem: github-actions 13 | directory: "/" 14 | schedule: 15 | interval: monthly 16 | target-branch: main 17 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | exclude: 3 | labels: 4 | - tagpr 5 | -------------------------------------------------------------------------------- /.github/workflows/tagpr-release.yml: -------------------------------------------------------------------------------- 1 | name: tagpr and release 2 | on: 3 | push: 4 | branches: ["main"] 5 | workflow_dispatch: 6 | inputs: 7 | tag: 8 | description: "release tag" 9 | required: false 10 | type: string 11 | 12 | permissions: 13 | pull-requests: write 14 | packages: write 15 | contents: write 16 | actions: write 17 | 18 | jobs: 19 | deploy: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v4 23 | with: 24 | ref: ${{ inputs.tag || github.ref }} 25 | - uses: Songmu/tagpr@v1 26 | id: tagpr 27 | env: 28 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 29 | if: ${{ github.event_name != 'workflow_dispatch' }} # skip on workflow_dispatch 30 | # after tagpr adds a release tag, or workflow_dispatch, release it 31 | - name: Set up Go 32 | uses: actions/setup-go@v5 33 | with: 34 | go-version: "1.24" 35 | if: ${{ steps.tagpr.outputs.tag != '' || github.event_name == 'workflow_dispatch' }} 36 | - name: Run GoReleaser 37 | uses: goreleaser/goreleaser-action@v6 38 | with: 39 | version: '~> v2' 40 | args: release 41 | env: 42 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 43 | if: ${{ steps.tagpr.outputs.tag != '' || github.event_name == 'workflow_dispatch' }} 44 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | on: [push, pull_request] 3 | jobs: 4 | test: 5 | strategy: 6 | matrix: 7 | go: 8 | - "1.23" 9 | - "1.24" 10 | name: Build 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: setup minio 14 | run: | 15 | wget -q https://dl.min.io/server/minio/release/linux-amd64/minio 16 | sudo install minio /usr/local/bin/ 17 | mkdir -p /tmp/data 18 | minio server /tmp/data & 19 | env: 20 | MINIO_ROOT_USER: testtest 21 | MINIO_ROOT_PASSWORD: testtest 22 | MINIO_REGION: us-west-2 23 | 24 | - name: Set up Go 25 | uses: actions/setup-go@v5 26 | with: 27 | go-version: ${{ matrix.go }} 28 | id: go 29 | 30 | - name: Check out code into the Go module directory 31 | uses: actions/checkout@v4 32 | 33 | - name: setup minio bucket 34 | run: | 35 | aws s3api create-bucket --bucket mybucket 36 | aws s3 cp tfstate/test/terraform.tfstate s3://mybucket/terraform.tfstate 37 | env: 38 | AWS_ACCESS_KEY_ID: testtest # same as MINIO_ROOT_USER 39 | AWS_SECRET_ACCESS_KEY: testtest # same as MINIO_ROOT_PASSWORD 40 | AWS_REGION: us-west-2 # same as MINIO_REGION 41 | AWS_EC2_METADATA_DISABLED: true 42 | AWS_ENDPOINT_URL_S3: http://127.0.0.1:9000 43 | 44 | - name: Build & Test 45 | run: | 46 | go test -race -v ./... 47 | env: 48 | TEST_AWS_ENDPOINT_URL_S3: http://127.0.0.1:9000 49 | AWS_ACCESS_KEY_ID: testtest # same as MINIO_ROOT_USER 50 | AWS_SECRET_ACCESS_KEY: testtest # same as MINIO_ROOT_PASSWORD 51 | AWS_REGION: us-west-2 # same as MINIO_REGION 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Binary built with `go build` 12 | cmd/tfstate-lookup/tfstate-lookup 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Dependency directories (remove the comment below to include it) 18 | vendor/ 19 | dist/ 20 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # This is an example goreleaser.yaml file with some sane defaults. 2 | # Make sure to check the documentation at http://goreleaser.com 3 | version: 2 4 | before: 5 | hooks: 6 | - go mod download 7 | builds: 8 | - env: 9 | - CGO_ENABLED=0 10 | main: ./cmd/tfstate-lookup/main.go 11 | goos: 12 | - darwin 13 | - linux 14 | goarch: 15 | - amd64 16 | - arm64 17 | archives: 18 | checksum: 19 | name_template: "checksums.txt" 20 | snapshot: 21 | version_template: "{{ .Tag }}-next" 22 | changelog: 23 | sort: asc 24 | filters: 25 | exclude: 26 | - "^docs:" 27 | - "^test:" 28 | -------------------------------------------------------------------------------- /.tagpr: -------------------------------------------------------------------------------- 1 | # config file for the tagpr in git config format 2 | # The tagpr generates the initial configuration, which you can rewrite to suit your environment. 3 | # CONFIGURATIONS: 4 | # tagpr.releaseBranch 5 | # Generally, it is "main." It is the branch for releases. The tagpr tracks this branch, 6 | # creates or updates a pull request as a release candidate, or tags when they are merged. 7 | # 8 | # tagpr.versionFile 9 | # Versioning file containing the semantic version needed to be updated at release. 10 | # It will be synchronized with the "git tag". 11 | # Often this is a meta-information file such as gemspec, setup.cfg, package.json, etc. 12 | # Sometimes the source code file, such as version.go or Bar.pm, is used. 13 | # If you do not want to use versioning files but only git tags, specify the "-" string here. 14 | # You can specify multiple version files by comma separated strings. 15 | # 16 | # tagpr.vPrefix 17 | # Flag whether or not v-prefix is added to semver when git tagging. (e.g. v1.2.3 if true) 18 | # This is only a tagging convention, not how it is described in the version file. 19 | # 20 | # tagpr.changelog (Optional) 21 | # Flag whether or not changelog is added or changed during the release. 22 | # 23 | # tagpr.command (Optional) 24 | # Command to change files just before release. 25 | # 26 | # tagpr.template (Optional) 27 | # Pull request template file in go template format 28 | # 29 | # tagpr.templateText (Optional) 30 | # Pull request template text in go template format 31 | # 32 | # tagpr.release (Optional) 33 | # GitHub Release creation behavior after tagging [true, draft, false] 34 | # If this value is not set, the release is to be created. 35 | # 36 | # tagpr.majorLabels (Optional) 37 | # Label of major update targets. Default is [major] 38 | # 39 | # tagpr.minorLabels (Optional) 40 | # Label of minor update targets. Default is [minor] 41 | # 42 | # tagpr.commitPrefix (Optional) 43 | # Prefix of commit message. Default is "[tagpr]" 44 | # 45 | [tagpr] 46 | vPrefix = true 47 | releaseBranch = main 48 | versionFile = - 49 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [v1.6.0](https://github.com/fujiwara/tfstate-lookup/compare/v1.5.0...v1.6.0) - 2025-04-19 4 | - Bump golang.org/x/crypto from 0.31.0 to 0.35.0 by @dependabot in https://github.com/fujiwara/tfstate-lookup/pull/235 5 | - Bump golang.org/x/net from 0.33.0 to 0.38.0 by @dependabot in https://github.com/fujiwara/tfstate-lookup/pull/236 6 | - Bump github.com/golang-jwt/jwt/v4 from 4.5.1 to 4.5.2 by @dependabot in https://github.com/fujiwara/tfstate-lookup/pull/233 7 | - Bump github.com/golang-jwt/jwt/v5 from 5.2.1 to 5.2.2 by @dependabot in https://github.com/fujiwara/tfstate-lookup/pull/232 8 | - Bump the aws-sdk-go-v2 group across 1 directory with 6 updates by @dependabot in https://github.com/fujiwara/tfstate-lookup/pull/238 9 | 10 | ## [v1.5.0](https://github.com/fujiwara/tfstate-lookup/compare/v1.4.3...v1.5.0) - 2025-02-07 11 | - Supports S3 endpoint by @fujiwara in https://github.com/fujiwara/tfstate-lookup/pull/223 12 | - use tagpr by @fujiwara in https://github.com/fujiwara/tfstate-lookup/pull/224 13 | - Support for `TF_WORKSPACE` by @shmokmt in https://github.com/fujiwara/tfstate-lookup/pull/212 14 | - update readme for -s3-endpoint-url and TF_WORKSPACE by @fujiwara in https://github.com/fujiwara/tfstate-lookup/pull/226 15 | 16 | ## [v1.4.3](https://github.com/fujiwara/tfstate-lookup/compare/v1.4.2...v1.4.3) - 2025-01-29 17 | - delete blank code block by @shmokmt in https://github.com/fujiwara/tfstate-lookup/pull/211 18 | - Bump golang.org/x/crypto from 0.24.0 to 0.31.0 by @dependabot in https://github.com/fujiwara/tfstate-lookup/pull/215 19 | - Bump github.com/hashicorp/go-slug from 0.15.0 to 0.16.3 by @dependabot in https://github.com/fujiwara/tfstate-lookup/pull/216 20 | - Bump golang.org/x/net from 0.26.0 to 0.33.0 by @dependabot in https://github.com/fujiwara/tfstate-lookup/pull/218 21 | - Bump github.com/golang-jwt/jwt/v4 from 4.5.0 to 4.5.1 by @dependabot in https://github.com/fujiwara/tfstate-lookup/pull/217 22 | - Embed the original error into a new error. by @fujiwara in https://github.com/fujiwara/tfstate-lookup/pull/214 23 | 24 | ## [v1.4.2](https://github.com/fujiwara/tfstate-lookup/compare/v1.4.1...v1.4.2) - 2024-09-17 25 | - Improve quoteJQQuery reliability by @qgrp-m-kondratenko in https://github.com/fujiwara/tfstate-lookup/pull/205 26 | - Fix/performance by @fujiwara in https://github.com/fujiwara/tfstate-lookup/pull/206 27 | 28 | ## [v1.4.1](https://github.com/fujiwara/tfstate-lookup/compare/v1.4.0...v1.4.1) - 2024-09-03 29 | - Add -dump flag to cli. by @fujiwara in https://github.com/fujiwara/tfstate-lookup/pull/204 30 | 31 | ## [v1.4.0](https://github.com/fujiwara/tfstate-lookup/compare/v1.3.2...v1.4.0) - 2024-09-03 32 | - Bump github.com/hashicorp/go-retryablehttp from 0.7.5 to 0.7.7 by @dependabot in https://github.com/fujiwara/tfstate-lookup/pull/189 33 | - add -dump flag and Dump() method. by @fujiwara in https://github.com/fujiwara/tfstate-lookup/pull/203 34 | 35 | ## [v1.3.2](https://github.com/fujiwara/tfstate-lookup/compare/v1.3.1...v1.3.2) - 2024-06-14 36 | - Add *TFState.FuncMap and *TFState.JsonnetNativeFuncs by @fujiwara in https://github.com/fujiwara/tfstate-lookup/pull/188 37 | 38 | ## [v1.3.1](https://github.com/fujiwara/tfstate-lookup/compare/v1.3.0...v1.3.1) - 2024-06-13 39 | 40 | ## [v1.3.0](https://github.com/fujiwara/tfstate-lookup/compare/v1.2.0...v1.3.0) - 2024-06-13 41 | - fix readme by @fujiwara in https://github.com/fujiwara/tfstate-lookup/pull/180 42 | - add JsonnetNatevieFuncs by @fujiwara in https://github.com/fujiwara/tfstate-lookup/pull/185 43 | - Bump actions/setup-go from 4 to 5 by @dependabot in https://github.com/fujiwara/tfstate-lookup/pull/181 44 | - Bump goreleaser/goreleaser-action from 5 to 6 by @dependabot in https://github.com/fujiwara/tfstate-lookup/pull/182 45 | - Bump golang.org/x/net from 0.20.0 to 0.23.0 by @dependabot in https://github.com/fujiwara/tfstate-lookup/pull/163 46 | - remove pkg/errors by @fujiwara in https://github.com/fujiwara/tfstate-lookup/pull/186 47 | - Bump the aws-sdk-go-v2 group with 6 updates by @dependabot in https://github.com/fujiwara/tfstate-lookup/pull/183 48 | - Bump github.com/hashicorp/go-tfe from 1.2.0 to 1.56.0 by @dependabot in https://github.com/fujiwara/tfstate-lookup/pull/184 49 | - Bump github.com/Azure/azure-sdk-for-go/sdk/azidentity from 1.3.1 to 1.6.0 by @dependabot in https://github.com/fujiwara/tfstate-lookup/pull/178 50 | - Update modules by @fujiwara in https://github.com/fujiwara/tfstate-lookup/pull/187 51 | 52 | ## [v1.2.0](https://github.com/fujiwara/tfstate-lookup/compare/v1.1.7...v1.2.0) - 2024-03-19 53 | - Integrate simeji/jid by @fujiwara in https://github.com/fujiwara/tfstate-lookup/pull/152 54 | 55 | ## [v1.1.7](https://github.com/fujiwara/tfstate-lookup/compare/v1.1.6...v1.1.7) - 2024-03-14 56 | - Bump google.golang.org/protobuf from 1.26.0 to 1.33.0 by @dependabot in https://github.com/fujiwara/tfstate-lookup/pull/148 57 | - Bump golang.org/x/net from 0.14.0 to 0.17.0 by @dependabot in https://github.com/fujiwara/tfstate-lookup/pull/149 58 | - Bump golang.org/x/crypto from 0.12.0 to 0.17.0 by @dependabot in https://github.com/fujiwara/tfstate-lookup/pull/150 59 | - update google.golang.org/grpc by @fujiwara in https://github.com/fujiwara/tfstate-lookup/pull/151 60 | 61 | ## [v1.1.6](https://github.com/fujiwara/tfstate-lookup/compare/v1.1.5...v1.1.6) - 2023-12-25 62 | - Fix bucket region configuration issue by @fujiwara in https://github.com/fujiwara/tfstate-lookup/pull/147 63 | 64 | ## [v1.1.5](https://github.com/fujiwara/tfstate-lookup/compare/v1.1.4...v1.1.5) - 2023-11-16 65 | - azurerm: Add AzureAD Auth by @jkroepke in https://github.com/fujiwara/tfstate-lookup/pull/144 66 | - Fix function names for azure by @fujiwara in https://github.com/fujiwara/tfstate-lookup/pull/145 67 | 68 | ## [v1.1.4](https://github.com/fujiwara/tfstate-lookup/compare/v1.1.3...v1.1.4) - 2023-10-06 69 | - Fix read tfe state. by @fujiwara in https://github.com/fujiwara/tfstate-lookup/pull/141 70 | 71 | ## [v1.1.3](https://github.com/fujiwara/tfstate-lookup/compare/v1.1.2...v1.1.3) - 2023-08-22 72 | - Upgrade azidentity by @jkroepke in https://github.com/fujiwara/tfstate-lookup/pull/133 73 | 74 | ## [v1.1.2](https://github.com/fujiwara/tfstate-lookup/compare/v1.1.1...v1.1.2) - 2023-05-19 75 | - Support for Subscription ID in AzureRM URL by @ashpr in https://github.com/fujiwara/tfstate-lookup/pull/115 76 | 77 | ## [v1.1.1](https://github.com/fujiwara/tfstate-lookup/compare/v1.1.0...v1.1.1) - 2023-04-04 78 | - Use azidentity for authentication by @jkroepke in https://github.com/fujiwara/tfstate-lookup/pull/86 79 | 80 | ## [v1.1.0](https://github.com/fujiwara/tfstate-lookup/compare/v1.0.0...v1.1.0) - 2023-03-24 81 | - Bump github.com/google/go-cmp from 0.5.8 to 0.5.9 by @dependabot in https://github.com/fujiwara/tfstate-lookup/pull/58 82 | - Bump github.com/aws/aws-sdk-go-v2 from 1.16.16 to 1.17.5 by @dependabot in https://github.com/fujiwara/tfstate-lookup/pull/56 83 | - Bump github.com/aws/aws-sdk-go-v2/feature/s3/manager from 1.11.33 to 1.11.54 by @dependabot in https://github.com/fujiwara/tfstate-lookup/pull/55 84 | - Bump github.com/Azure/azure-storage-blob-go from 0.14.0 to 0.15.0 by @dependabot in https://github.com/fujiwara/tfstate-lookup/pull/54 85 | - Bump github.com/mattn/go-isatty from 0.0.12 to 0.0.17 by @dependabot in https://github.com/fujiwara/tfstate-lookup/pull/52 86 | - Bump github.com/itchyny/gojq from 0.9.0 to 0.12.11 by @dependabot in https://github.com/fujiwara/tfstate-lookup/pull/53 87 | - Bump github.com/Azure/go-autorest/autorest/azure/cli from 0.4.2 to 0.4.6 by @dependabot in https://github.com/fujiwara/tfstate-lookup/pull/49 88 | - Bump github.com/Azure/azure-sdk-for-go from 56.2.0+incompatible to 68.0.0+incompatible by @dependabot in https://github.com/fujiwara/tfstate-lookup/pull/57 89 | - Add interactive mode to tfstate-lookup command and refactor object display. by @fujiwara in https://github.com/fujiwara/tfstate-lookup/pull/79 90 | 91 | ## [v1.0.0](https://github.com/fujiwara/tfstate-lookup/compare/v0.4.4...v1.0.0) - 2022-09-21 92 | - switch to aws-sdk-go-v2 by @fujiwara in https://github.com/fujiwara/tfstate-lookup/pull/47 93 | - introduce context. by @fujiwara in https://github.com/fujiwara/tfstate-lookup/pull/48 94 | 95 | ## [v0.4.4](https://github.com/fujiwara/tfstate-lookup/compare/v0.4.3...v0.4.4) - 2022-06-10 96 | - fix lookup to azurerm subdir. by @fujiwara in https://github.com/fujiwara/tfstate-lookup/pull/43 97 | - fixes #39 by @fujiwara in https://github.com/fujiwara/tfstate-lookup/pull/45 98 | - fixes #40 by @fujiwara in https://github.com/fujiwara/tfstate-lookup/pull/44 99 | - Support encryption_key in GCS backend by @dex4er in https://github.com/fujiwara/tfstate-lookup/pull/38 100 | 101 | ## [v0.4.3](https://github.com/fujiwara/tfstate-lookup/compare/v0.4.2...v0.4.3) - 2022-05-06 102 | - use StateVersions.ReadCurrent instead of Current by @Songmu in https://github.com/fujiwara/tfstate-lookup/pull/41 103 | 104 | ## [v0.4.2](https://github.com/fujiwara/tfstate-lookup/compare/v0.4.1...v0.4.2) - 2021-12-14 105 | - multiple resources may have same name (for_each or not). by @fujiwara in https://github.com/fujiwara/tfstate-lookup/pull/36 106 | 107 | ## [v0.4.1](https://github.com/fujiwara/tfstate-lookup/compare/v0.4.0...v0.4.1) - 2021-12-13 108 | - Fix lookup list resources (some resources have not index_key). by @fujiwara in https://github.com/fujiwara/tfstate-lookup/pull/35 109 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test install 2 | 3 | test: 4 | go test ./... 5 | 6 | install: 7 | go install github.com/fujiwara/tfstate-lookup/cmd/tfstate-lookup 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tfstate-lookup 2 | 3 | Lookup resource attributes in tfstate. 4 | 5 | ## Install 6 | 7 | ### homebrew 8 | 9 | ``` 10 | $ brew install fujiwara/tap/tfstate-lookup 11 | ``` 12 | 13 | ### [Binary releases](https://github.com/fujiwara/tfstate-lookup/releases) 14 | 15 | ## Usage (command) 16 | 17 | ``` 18 | Usage of tfstate-lookup: 19 | -dump 20 | dump all resources 21 | -i interactive mode 22 | -j run jid after selecting an item 23 | -s string 24 | tfstate file path or URL (default "terraform.tfstate") 25 | -s3-endpoint-url string 26 | S3 endpoint URL 27 | -state string 28 | tfstate file path or URL (default "terraform.tfstate") 29 | -timeout duration 30 | timeout for reading tfstate 31 | ``` 32 | 33 | Supported URL schemes are http(s), s3, gs, azurerm, file or remote (for Terraform Cloud and Terraform Enterprise). 34 | 35 | ```console 36 | $ tfstate-lookup -s .terraform/terraform.tfstate aws_vpc.main.id 37 | vpc-1a2b3c4d 38 | 39 | $ tfstate-lookup aws_vpc.main 40 | { 41 | "arn": "arn:aws:ec2:ap-northeast-1:123456789012:vpc/vpc-1a2b3c4d", 42 | "assign_generated_ipv6_cidr_block": false, 43 | "cidr_block": "10.0.0.0/16", 44 | "default_network_acl_id": "acl-001234567890abcde", 45 | "default_route_table_id": "rtb-001234567890abcde", 46 | "default_security_group_id": "sg-01234567890abcdef", 47 | "dhcp_options_id": "dopt-64569903", 48 | "enable_classiclink": false, 49 | "enable_classiclink_dns_support": false, 50 | "enable_dns_hostnames": true, 51 | "enable_dns_support": true, 52 | "id": "vpc-1a2b3c4d", 53 | "instance_tenancy": "default", 54 | "ipv6_association_id": "", 55 | "ipv6_cidr_block": "", 56 | "main_route_table_id": "rtb-001234567890abcde", 57 | "owner_id": "123456789012", 58 | "tags": { 59 | "Name": "main" 60 | } 61 | } 62 | ``` 63 | 64 | A remote state is supported only S3, GCS, AzureRM and Terraform Cloud / Terraform Enterprise backend currently. 65 | 66 | ### Interactive mode 67 | 68 | You can use interactive mode with `-i` option. 69 | 70 | ```console 71 | $ tfstate-lookup -i 72 | Search: █ 73 | ? Select an item: 74 | ▸ aws_acm_certificate.foo 75 | aws_acm_certificate_validation.foo 76 | aws_cloudwatch_log_group.foo 77 | aws_ecs_cluster.foo 78 | ... 79 | ``` 80 | 81 | When you select an item, it shows the attributes of the resource. 82 | 83 | ### Run jid after selecting an item 84 | 85 | You can run [jid](https://github.com/simeji/jid) after selecting an item with `-j` option. 86 | 87 | jid is a JSON incremental digger. 88 | 89 | `tfstate-lookup -i -j` runs jid after selecting an item. 90 | 91 | `tfstate-lookup -j some.resource` runs jid for the attributes of the resource. 92 | 93 | tfstate-lookup integrates jid as a library, so you don't need to install jid command. 94 | 95 | See also [simiji/jid](https://github.com/simeji/jid). 96 | 97 | ### Dump all resources, outputs, and data sources in tfstate 98 | 99 | You can dump all resources, outputs, and data sources in tfstate with `-dump` option. 100 | 101 | ```console 102 | $ tfstate-lookup -dump 103 | ``` 104 | 105 | The output is a JSON object. The keys are the address of the resource, output, or data source. The values are the same as the lookup result. 106 | 107 | ```jsonnet 108 | { 109 | "aws_vpc.main": { 110 | "arn": "arn:aws:ec2:ap-northeast-1:123456789012:vpc/vpc-1a2b3c4d", 111 | "assign_generated_ipv6_cidr_block": false, 112 | // ... 113 | }, 114 | "data.aws_ami.foo": { 115 | "arn": "arn:aws:ec2:ap-northeast-1:123456789012:ami/ami-1a2b3c4d", 116 | // ... 117 | }, 118 | "output.foo": "bar", 119 | // ... 120 | } 121 | ``` 122 | 123 | ## Usage (Go package) 124 | 125 | See details in [godoc](https://pkg.go.dev/github.com/fujiwara/tfstate-lookup/tfstate). 126 | 127 | ```go 128 | package main 129 | 130 | import( 131 | "fmt" 132 | "os" 133 | 134 | "github.com/fujiwara/tfstate-lookup/tfstate" 135 | ) 136 | 137 | func main() { 138 | state, _ := tfstate.ReadURL(ctx, "s3://mybucket/terraform.tfstate") 139 | attrs, _ := state.Lookup("aws_vpc.main.id") 140 | fmt.Println(attrs.String()) 141 | } 142 | ``` 143 | 144 | 145 | ## Supported tfstate URL format 146 | 147 | - Local file `file://path/to/terraform.tfstate` 148 | - HTTP/HTTPS `https://example.com/terraform.tfstate` 149 | - Amazon S3 `s3://{bucket}/{key}` 150 | - Terraform Cloud `remote://app.terraform.io/{organization}/{workspaces}` 151 | - `TFE_TOKEN` environment variable is required. 152 | - Google Cloud Storage `gs://{bucket}/{key}` 153 | - Azure Blog Storage 154 | - `azurerm://{resource_group_name}/{storage_account_name}/{container_name}/{blob_name}` 155 | - `azurerm://{subscription_id}@{resource_group_name}/{storage_account_name}/{container_name}/{blob_name}` 156 | 157 | ### S3 endpoint URL support 158 | 159 | You can specify the S3 endpoint URL with `-s3-endpoint-url` option. `AWS_ENDPOINT_URL_S3` environment variable is also supported. 160 | 161 | ```console 162 | $ tfstate-lookup -s3-endpoint-url http://localhost:9000 s3://mybucket/terraform.tfstate 163 | 164 | $ AWS_ENDPOINT_URL_S3=http://localhost:9000 tfstate-lookup s3://mybucket/terraform.tfstate 165 | ``` 166 | 167 | This option is useful for S3 compatible storage services. 168 | 169 | ### Terraform Workspace support 170 | 171 | You can specify the Terraform workspace with `TF_WORKSPACE` environment variable. 172 | 173 | ## LICENSE 174 | 175 | [Mozilla Public License Version 2.0](LICENSE) 176 | -------------------------------------------------------------------------------- /cmd/tfstate-lookup/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "encoding/json" 7 | "flag" 8 | "fmt" 9 | "os" 10 | "strings" 11 | "time" 12 | 13 | "github.com/fujiwara/tfstate-lookup/tfstate" 14 | "github.com/manifoldco/promptui" 15 | "github.com/mattn/go-isatty" 16 | "github.com/simeji/jid" 17 | ) 18 | 19 | var DefaultStateFiles = []string{ 20 | "terraform.tfstate", 21 | ".terraform/terraform.tfstate", 22 | } 23 | 24 | func main() { 25 | if err := _main(); err != nil { 26 | fmt.Fprintln(os.Stderr, err.Error()) 27 | os.Exit(1) 28 | } 29 | } 30 | 31 | func _main() error { 32 | var ( 33 | stateLoc string 34 | defaultStateFile = DefaultStateFiles[0] 35 | interactive bool 36 | runJid bool 37 | dump bool 38 | timeout time.Duration 39 | s3EndpointURL string 40 | ) 41 | for _, name := range DefaultStateFiles { 42 | if _, err := os.Stat(name); err == nil { 43 | defaultStateFile = name 44 | break 45 | } 46 | } 47 | 48 | flag.StringVar(&stateLoc, "state", defaultStateFile, "tfstate file path or URL") 49 | flag.StringVar(&stateLoc, "s", defaultStateFile, "tfstate file path or URL") 50 | flag.BoolVar(&interactive, "i", false, "interactive mode") 51 | flag.BoolVar(&runJid, "j", false, "run jid after selecting an item") 52 | flag.BoolVar(&dump, "dump", false, "dump all resources") 53 | flag.StringVar(&s3EndpointURL, "s3-endpoint-url", "", "S3 endpoint URL") 54 | flag.DurationVar(&timeout, "timeout", 0, "timeout for reading tfstate") 55 | flag.Parse() 56 | 57 | if s3EndpointURL != "" { 58 | os.Setenv(tfstate.S3EndpointEnvKey, s3EndpointURL) 59 | } 60 | 61 | var ctx = context.Background() 62 | var cancel context.CancelFunc 63 | if timeout > 0 { 64 | ctx, cancel = context.WithTimeout(ctx, timeout) 65 | defer cancel() 66 | } 67 | 68 | state, err := tfstate.ReadURL(ctx, stateLoc) 69 | if err != nil { 70 | return err 71 | } 72 | var key string 73 | if len(flag.Args()) > 0 { 74 | key = flag.Arg(0) 75 | } else { 76 | if dump { 77 | return dumpObjects(state) 78 | } 79 | // list 80 | names, err := state.List() 81 | if err != nil { 82 | return err 83 | } 84 | if !interactive { 85 | fmt.Println(strings.Join(names, "\n")) 86 | return nil 87 | } 88 | key, err = promptForSelection(names) 89 | if err != nil { 90 | return err 91 | } 92 | } 93 | 94 | obj, err := state.Lookup(key) 95 | if err != nil { 96 | return err 97 | } 98 | if runJid { 99 | return jidObject(obj) 100 | } else { 101 | return printObject(obj) 102 | } 103 | } 104 | 105 | func dumpObjects(state *tfstate.TFState) error { 106 | res, err := state.Dump() 107 | if err != nil { 108 | return err 109 | } 110 | enc := json.NewEncoder(os.Stdout) 111 | enc.SetIndent("", " ") 112 | return enc.Encode(res) 113 | } 114 | 115 | func printObject(obj *tfstate.Object) error { 116 | b := obj.Bytes() 117 | w := os.Stdout 118 | if isatty.IsTerminal(w.Fd()) && (bytes.HasPrefix(b, []byte("[")) || bytes.HasPrefix(b, []byte("{"))) { 119 | var out bytes.Buffer 120 | json.Indent(&out, b, "", " ") 121 | out.WriteRune('\n') 122 | _, err := out.WriteTo(w) 123 | return err 124 | } else { 125 | _, err := fmt.Fprintln(w, string(b)) 126 | return err 127 | } 128 | } 129 | 130 | func promptForSelection(choices []string) (string, error) { 131 | prompt := promptui.Select{ 132 | Label: "Select an item", 133 | Items: choices, 134 | StartInSearchMode: true, 135 | Size: 20, 136 | Searcher: func(input string, index int) bool { 137 | return strings.Contains(choices[index], input) 138 | }, 139 | } 140 | _, result, err := prompt.Run() 141 | if err != nil { 142 | return "", err 143 | } 144 | return result, nil 145 | } 146 | 147 | func jidObject(obj *tfstate.Object) error { 148 | ea := &jid.EngineAttribute{ 149 | DefaultQuery: ".", 150 | Monochrome: false, 151 | PrettyResult: true, 152 | } 153 | r := bytes.NewReader(obj.Bytes()) 154 | e, err := jid.NewEngine(r, ea) 155 | if err != nil { 156 | return err 157 | } 158 | result := e.Run() 159 | if err := result.GetError(); err != nil { 160 | return err 161 | } 162 | fmt.Println(result.GetContent()) 163 | return nil 164 | } 165 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/fujiwara/tfstate-lookup 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.24.2 6 | 7 | require ( 8 | cloud.google.com/go/storage v1.36.0 9 | github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 10 | github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.3.0 11 | github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.1.0 12 | github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 13 | github.com/aws/aws-sdk-go-v2 v1.36.3 14 | github.com/aws/aws-sdk-go-v2/config v1.29.14 15 | github.com/aws/aws-sdk-go-v2/credentials v1.17.67 16 | github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.72 17 | github.com/aws/aws-sdk-go-v2/service/s3 v1.79.2 18 | github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 19 | github.com/google/go-cmp v0.6.0 20 | github.com/google/go-jsonnet v0.20.0 21 | github.com/hashicorp/go-tfe v1.56.0 22 | github.com/itchyny/gojq v0.12.16 23 | github.com/manifoldco/promptui v0.9.0 24 | github.com/mattn/go-isatty v0.0.20 25 | github.com/simeji/jid v0.7.6 26 | google.golang.org/api v0.155.0 27 | ) 28 | 29 | require ( 30 | cloud.google.com/go v0.112.0 // indirect 31 | cloud.google.com/go/compute v1.23.3 // indirect 32 | cloud.google.com/go/compute/metadata v0.2.3 // indirect 33 | cloud.google.com/go/iam v1.1.5 // indirect 34 | github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 // indirect 35 | github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 // indirect 36 | github.com/Azure/go-autorest v14.2.0+incompatible // indirect 37 | github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect 38 | github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect 39 | github.com/Azure/go-autorest/logger v0.2.1 // indirect 40 | github.com/Azure/go-autorest/tracing v0.6.0 // indirect 41 | github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect 42 | github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 // indirect 43 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect 44 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect 45 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect 46 | github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect 47 | github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 // indirect 48 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect 49 | github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0 // indirect 50 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect 51 | github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 // indirect 52 | github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 // indirect 53 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 // indirect 54 | github.com/aws/smithy-go v1.22.2 // indirect 55 | github.com/bitly/go-simplejson v0.5.0 // indirect 56 | github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect 57 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect 58 | github.com/dimchansky/utfbom v1.1.1 // indirect 59 | github.com/fatih/color v1.16.0 // indirect 60 | github.com/felixge/httpsnoop v1.0.4 // indirect 61 | github.com/go-logr/logr v1.3.0 // indirect 62 | github.com/go-logr/stdr v1.2.2 // indirect 63 | github.com/golang-jwt/jwt/v4 v4.5.2 // indirect 64 | github.com/golang-jwt/jwt/v5 v5.2.2 // indirect 65 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 66 | github.com/golang/protobuf v1.5.3 // indirect 67 | github.com/google/go-querystring v1.1.0 // indirect 68 | github.com/google/s2a-go v0.1.7 // indirect 69 | github.com/google/uuid v1.6.0 // indirect 70 | github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect 71 | github.com/googleapis/gax-go/v2 v2.12.0 // indirect 72 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect 73 | github.com/hashicorp/go-retryablehttp v0.7.7 // indirect 74 | github.com/hashicorp/go-slug v0.16.3 // indirect 75 | github.com/hashicorp/go-version v1.6.0 // indirect 76 | github.com/hashicorp/jsonapi v1.3.1 // indirect 77 | github.com/itchyny/timefmt-go v0.1.6 // indirect 78 | github.com/kylelemons/godebug v1.1.0 // indirect 79 | github.com/mattn/go-colorable v0.1.13 // indirect 80 | github.com/mattn/go-runewidth v0.0.15 // indirect 81 | github.com/mitchellh/go-homedir v1.1.0 // indirect 82 | github.com/nsf/termbox-go v0.0.0-20181027232701-60ab7e3d12ed // indirect 83 | github.com/nwidger/jsoncolor v0.0.0-20170215171346-75a6de4340e5 // indirect 84 | github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect 85 | github.com/pkg/errors v0.9.1 // indirect 86 | github.com/rivo/uniseg v0.4.7 // indirect 87 | go.opencensus.io v0.24.0 // indirect 88 | go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect 89 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect 90 | go.opentelemetry.io/otel v1.21.0 // indirect 91 | go.opentelemetry.io/otel/metric v1.21.0 // indirect 92 | go.opentelemetry.io/otel/trace v1.21.0 // indirect 93 | golang.org/x/crypto v0.36.0 // indirect 94 | golang.org/x/net v0.38.0 // indirect 95 | golang.org/x/oauth2 v0.16.0 // indirect 96 | golang.org/x/sync v0.12.0 // indirect 97 | golang.org/x/sys v0.31.0 // indirect 98 | golang.org/x/text v0.23.0 // indirect 99 | golang.org/x/time v0.5.0 // indirect 100 | google.golang.org/appengine v1.6.8 // indirect 101 | google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect 102 | google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 // indirect 103 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect 104 | google.golang.org/grpc v1.62.1 // indirect 105 | google.golang.org/protobuf v1.33.0 // indirect 106 | gopkg.in/yaml.v2 v2.4.0 // indirect 107 | sigs.k8s.io/yaml v1.1.0 // indirect 108 | ) 109 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM= 3 | cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= 4 | cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= 5 | cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= 6 | cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= 7 | cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= 8 | cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI= 9 | cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= 10 | cloud.google.com/go/storage v1.36.0 h1:P0mOkAcaJxhCTvAkMhxMfrTKiNcub4YmmPBtlhAyTr8= 11 | cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= 12 | github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 h1:E+OJmp2tPvt1W+amx48v1eqbjDYsgN+RzP4q16yV5eM= 13 | github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1/go.mod h1:a6xsAQUZg+VsS3TJ05SRp524Hs4pZ/AeFSr5ENf0Yjo= 14 | github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 h1:U2rTu3Ef+7w9FHKIAXM6ZyqF3UOWJZ12zIm8zECAFfg= 15 | github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg= 16 | github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 h1:jBQA3cKT4L2rWMpgE7Yt3Hwh2aUj8KXjIGLxjHeYNNo= 17 | github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0/go.mod h1:4OG6tQ9EOP/MT0NMjDlRzWoVFxfu9rN9B2X+tlSVktg= 18 | github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2 h1:mLY+pNLjCUeKhgnAJWAKhEUQM+RJQo2H1fuGSw1Ky1E= 19 | github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2/go.mod h1:FbdwsQ2EzwvXxOPcMFYO8ogEc9uMMIj3YkmCdXdAFmk= 20 | github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 h1:ECsQtyERDVz3NP3kvDOTLvbQhqWp/x9EsGKtb4ogUr8= 21 | github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0/go.mod h1:s1tW/At+xHqjNFvWU4G0c0Qv33KOhvbGNj0RCTQDV8s= 22 | github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.3.0 h1:LcJtQjCXJUm1s7JpUHZvu+bpgURhCatxVNbGADXniX0= 23 | github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.3.0/go.mod h1:+OgGVo0Httq7N5oayfvaLQ/Jq+2gJdqfp++Hyyl7Tws= 24 | github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.1.0 h1:nVocQV40OQne5613EeLayJiRAJuKlBGy+m22qWG+WRg= 25 | github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.1.0/go.mod h1:7QJP7dr2wznCMeqIrhMgWGf7XpAQnVrJqDm9nvV3Cu4= 26 | github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= 27 | github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= 28 | github.com/Azure/go-autorest/autorest/adal v0.9.18 h1:kLnPsRjzZZUF3K5REu/Kc+qMQrvuza2bwSnNdhmzLfQ= 29 | github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= 30 | github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 h1:w77/uPk80ZET2F+AfQExZyEWtn+0Rk/uw17m9fv5Ajc= 31 | github.com/Azure/go-autorest/autorest/azure/cli v0.4.6/go.mod h1:piCfgPho7BiIDdEQ1+g4VmKyD5y+p/XtSNqE6Hc4QD0= 32 | github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= 33 | github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= 34 | github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= 35 | github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= 36 | github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= 37 | github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= 38 | github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= 39 | github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= 40 | github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU= 41 | github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= 42 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 43 | github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= 44 | github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= 45 | github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 h1:zAybnyUQXIZ5mok5Jqwlf58/TFE7uvd3IAsa1aF9cXs= 46 | github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10/go.mod h1:qqvMj6gHLR/EXWZw4ZbqlPbQUyenf4h82UQUlKc+l14= 47 | github.com/aws/aws-sdk-go-v2/config v1.29.14 h1:f+eEi/2cKCg9pqKBoAIwRGzVb70MRKqWX4dg1BDcSJM= 48 | github.com/aws/aws-sdk-go-v2/config v1.29.14/go.mod h1:wVPHWcIFv3WO89w0rE10gzf17ZYy+UVS1Geq8Iei34g= 49 | github.com/aws/aws-sdk-go-v2/credentials v1.17.67 h1:9KxtdcIA/5xPNQyZRgUSpYOE6j9Bc4+D7nZua0KGYOM= 50 | github.com/aws/aws-sdk-go-v2/credentials v1.17.67/go.mod h1:p3C44m+cfnbv763s52gCqrjaqyPikj9Sg47kUVaNZQQ= 51 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw= 52 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M= 53 | github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.72 h1:PcKMOZfp+kNtJTw2HF2op6SjDvwPBYRvz0Y24PQLUR4= 54 | github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.72/go.mod h1:vq7/m7dahFXcdzWVOvvjasDI9RcsD3RsTfHmDundJYg= 55 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q= 56 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY= 57 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0= 58 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q= 59 | github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= 60 | github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= 61 | github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 h1:ZNTqv4nIdE/DiBfUUfXcLZ/Spcuz+RjeziUtNJackkM= 62 | github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34/go.mod h1:zf7Vcd1ViW7cPqYWEHLHJkS50X0JS2IKz9Cgaj6ugrs= 63 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE= 64 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA= 65 | github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0 h1:lguz0bmOoGzozP9XfRJR1QIayEYo+2vP/No3OfLF0pU= 66 | github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0/go.mod h1:iu6FSzgt+M2/x3Dk8zhycdIcHjEFb36IS8HVUVFoMg0= 67 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM= 68 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY= 69 | github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 h1:moLQUoVq91LiqT1nbvzDukyqAlCv89ZmwaHw/ZFlFZg= 70 | github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15/go.mod h1:ZH34PJUc8ApjBIfgQCFvkWcUDBtl/WTD+uiYHjd8igA= 71 | github.com/aws/aws-sdk-go-v2/service/s3 v1.79.2 h1:tWUG+4wZqdMl/znThEk9tcCy8tTMxq8dW0JTgamohrY= 72 | github.com/aws/aws-sdk-go-v2/service/s3 v1.79.2/go.mod h1:U5SNqwhXB3Xe6F47kXvWihPl/ilGaEDe8HD/50Z9wxc= 73 | github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 h1:1Gw+9ajCV1jogloEv1RRnvfRFia2cL6c9cuKV2Ps+G8= 74 | github.com/aws/aws-sdk-go-v2/service/sso v1.25.3/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI= 75 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 h1:hXmVKytPfTy5axZ+fYbR5d0cFmC3JvwLm5kM83luako= 76 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs= 77 | github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 h1:1XuUZ8mYJw9B6lzAkXhqHlJd/XvaX32evhproijJEZY= 78 | github.com/aws/aws-sdk-go-v2/service/sts v1.33.19/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4= 79 | github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= 80 | github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= 81 | github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y= 82 | github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= 83 | github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= 84 | github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= 85 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 86 | github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= 87 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 88 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= 89 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 90 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= 91 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 92 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 93 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 94 | github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ= 95 | github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= 96 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 97 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 98 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 99 | github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= 100 | github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= 101 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 102 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 103 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 104 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 105 | github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= 106 | github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= 107 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 108 | github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= 109 | github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= 110 | github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= 111 | github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 112 | github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 113 | github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= 114 | github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 115 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 116 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 117 | github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= 118 | github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= 119 | github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= 120 | github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= 121 | github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= 122 | github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= 123 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 124 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 125 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= 126 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 127 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 128 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 129 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 130 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 131 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 132 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 133 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 134 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 135 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 136 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 137 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 138 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 139 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 140 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 141 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 142 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 143 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 144 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 145 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 146 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 147 | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 148 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 149 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 150 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 151 | github.com/google/go-jsonnet v0.20.0 h1:WG4TTSARuV7bSm4PMB4ohjxe33IHT5WVTrJSU33uT4g= 152 | github.com/google/go-jsonnet v0.20.0/go.mod h1:VbgWF9JX7ztlv770x/TolZNGGFfiHEVx9G6ca2eUmeA= 153 | github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= 154 | github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= 155 | github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= 156 | github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= 157 | github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= 158 | github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= 159 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 160 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 161 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 162 | github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= 163 | github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= 164 | github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= 165 | github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= 166 | github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= 167 | github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= 168 | github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= 169 | github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= 170 | github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= 171 | github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= 172 | github.com/hashicorp/go-slug v0.16.3 h1:pe0PMwz2UWN1168QksdW/d7u057itB2gY568iF0E2Ns= 173 | github.com/hashicorp/go-slug v0.16.3/go.mod h1:THWVTAXwJEinbsp4/bBRcmbaO5EYNLTqxbG4tZ3gCYQ= 174 | github.com/hashicorp/go-tfe v1.56.0 h1:AjBTo7TmWoz42l4KhH65Q3NvjRD5yD3XZrG1tzFySeI= 175 | github.com/hashicorp/go-tfe v1.56.0/go.mod h1:XnTtBj3tVQ4uFkcFsv8Grn+O1CVcIcceL1uc2AgUcaU= 176 | github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= 177 | github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 178 | github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= 179 | github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 180 | github.com/hashicorp/jsonapi v1.3.1 h1:GtPvnmcWgYwCuDGvYT5VZBHcUyFdq9lSyCzDjn1DdPo= 181 | github.com/hashicorp/jsonapi v1.3.1/go.mod h1:kWfdn49yCjQvbpnvY1dxxAuAFzISwrrMDQOcu6NsFoM= 182 | github.com/itchyny/gojq v0.12.16 h1:yLfgLxhIr/6sJNVmYfQjTIv0jGctu6/DgDoivmxTr7g= 183 | github.com/itchyny/gojq v0.12.16/go.mod h1:6abHbdC2uB9ogMS38XsErnfqJ94UlngIJGlRAIj4jTM= 184 | github.com/itchyny/timefmt-go v0.1.6 h1:ia3s54iciXDdzWzwaVKXZPbiXzxxnv1SPGFfM/myJ5Q= 185 | github.com/itchyny/timefmt-go v0.1.6/go.mod h1:RRDZYC5s9ErkjQvTvvU7keJjxUYzIISJGxm9/mAERQg= 186 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 187 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 188 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 189 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 190 | github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= 191 | github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 192 | github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= 193 | github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= 194 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 195 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 196 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 197 | github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 198 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 199 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 200 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 201 | github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= 202 | github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= 203 | github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 204 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 205 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 206 | github.com/nsf/termbox-go v0.0.0-20181027232701-60ab7e3d12ed h1:bAVGG6B+R5qpSylrrA+BAMrzYkdAoiTaKPVxRB+4cyM= 207 | github.com/nsf/termbox-go v0.0.0-20181027232701-60ab7e3d12ed/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= 208 | github.com/nwidger/jsoncolor v0.0.0-20170215171346-75a6de4340e5 h1:d+C3xJdxZT7wNlxqEwbXn3R355CwAhYBL9raVNfSnK0= 209 | github.com/nwidger/jsoncolor v0.0.0-20170215171346-75a6de4340e5/go.mod h1:GYFm0zZgTNeoK1QxuIofRDasy2ibmaJZhZLzwsMXUF4= 210 | github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= 211 | github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= 212 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 213 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 214 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 215 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 216 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 217 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 218 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 219 | github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= 220 | github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 221 | github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= 222 | github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= 223 | github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= 224 | github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= 225 | github.com/simeji/jid v0.7.6 h1:AfqyVUyxhoMXjECfQ+0UoxLL+atEMM4DF6GjqKg+klM= 226 | github.com/simeji/jid v0.7.6/go.mod h1:aWC2wZw1IZvbeAh/UcbA86BznfPyxsMSVTcvzWX18q0= 227 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 228 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 229 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 230 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 231 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 232 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 233 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 234 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 235 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 236 | go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= 237 | go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= 238 | go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE= 239 | go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= 240 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24= 241 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= 242 | go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= 243 | go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= 244 | go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= 245 | go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= 246 | go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= 247 | go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= 248 | go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= 249 | go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= 250 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 251 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 252 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 253 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 254 | golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= 255 | golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= 256 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 257 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 258 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 259 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 260 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 261 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 262 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 263 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 264 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 265 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 266 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 267 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 268 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 269 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 270 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 271 | golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= 272 | golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= 273 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 274 | golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= 275 | golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= 276 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 277 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 278 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 279 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 280 | golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= 281 | golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 282 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 283 | golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 284 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 285 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 286 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 287 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 288 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 289 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 290 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 291 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 292 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 293 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 294 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 295 | golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= 296 | golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 297 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 298 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 299 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 300 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 301 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 302 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 303 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= 304 | golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= 305 | golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= 306 | golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= 307 | golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 308 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 309 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 310 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 311 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 312 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 313 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 314 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 315 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 316 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 317 | golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= 318 | golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= 319 | google.golang.org/api v0.155.0 h1:vBmGhCYs0djJttDNynWo44zosHlPvHmA0XiN2zP2DtA= 320 | google.golang.org/api v0.155.0/go.mod h1:GI5qK5f40kCpHfPn6+YzGAByIKWv8ujFnmoWm7Igduk= 321 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 322 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 323 | google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= 324 | google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= 325 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 326 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 327 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 328 | google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ= 329 | google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= 330 | google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 h1:Lj5rbfG876hIAYFjqiJnPHfhXbv+nzTWfm04Fg/XSVU= 331 | google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= 332 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM= 333 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= 334 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 335 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 336 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 337 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 338 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 339 | google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= 340 | google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= 341 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 342 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 343 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 344 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 345 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 346 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 347 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 348 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 349 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 350 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 351 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 352 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= 353 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 354 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 355 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 356 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 357 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 358 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 359 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 360 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 361 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 362 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 363 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 364 | sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= 365 | sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= 366 | -------------------------------------------------------------------------------- /tfstate/export_test.go: -------------------------------------------------------------------------------- 1 | package tfstate 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/aws/aws-sdk-go-v2/config" 7 | ) 8 | 9 | func GetBucketRegion(bucket string) (string, error) { 10 | ctx := context.Background() 11 | cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion("us-east-1")) 12 | if err != nil { 13 | return "", err 14 | } 15 | return getBucketRegion(ctx, cfg, bucket) 16 | } 17 | -------------------------------------------------------------------------------- /tfstate/funcs.go: -------------------------------------------------------------------------------- 1 | package tfstate 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "strings" 7 | "text/template" 8 | ) 9 | 10 | const ( 11 | defaultFuncName = "tfstate" 12 | ) 13 | 14 | // FuncMap provides a template.FuncMap tfstate based on URL and provide 15 | func FuncMap(ctx context.Context, stateLoc string) (template.FuncMap, error) { 16 | return FuncMapWithName(ctx, defaultFuncName, stateLoc) 17 | } 18 | 19 | // FuncMapWithName provides a template.FuncMap. can lockup values from tfstate. 20 | func FuncMapWithName(ctx context.Context, name string, stateLoc string) (template.FuncMap, error) { 21 | state, err := ReadURL(ctx, stateLoc) 22 | if err != nil { 23 | return nil, fmt.Errorf("failed to read tfstate: %s: %w", stateLoc, err) 24 | } 25 | return state.FuncMapWithName(ctx, name), nil 26 | } 27 | 28 | // MustFuncMap is similar to FuncMap, but panics if it cannot get and parse tfstate. 29 | func MustFuncMap(ctx context.Context, stateLoc string) template.FuncMap { 30 | return MustFuncMapWithName(ctx, defaultFuncName, stateLoc) 31 | } 32 | 33 | // MustFuncMapWithName is similar to FuncMapWithName, but panics if it cannot get and parse tfstate. 34 | func MustFuncMapWithName(ctx context.Context, name string, stateLoc string) template.FuncMap { 35 | funcMap, err := FuncMapWithName(ctx, name, stateLoc) 36 | if err != nil { 37 | panic(err) 38 | } 39 | return funcMap 40 | } 41 | 42 | func (s *TFState) FuncMapWithName(ctx context.Context, name string) template.FuncMap { 43 | nameFunc := func(addrs string) string { 44 | if strings.Contains(addrs, "'") { 45 | addrs = strings.ReplaceAll(addrs, "'", "\"") 46 | } 47 | attrs, err := s.Lookup(addrs) 48 | if err != nil { 49 | panic(fmt.Sprintf("failed to lookup %s in tfstate: %s", addrs, err)) 50 | } 51 | if attrs.Value == nil { 52 | panic(fmt.Sprintf("%s is not found in tfstate", addrs)) 53 | } 54 | return attrs.String() 55 | } 56 | return template.FuncMap{ 57 | name: nameFunc, 58 | name + "f": func(format string, args ...interface{}) string { 59 | addr := fmt.Sprintf(format, args...) 60 | return nameFunc(addr) 61 | }, 62 | } 63 | } 64 | 65 | func (s *TFState) FuncMap(ctx context.Context) template.FuncMap { 66 | return s.FuncMapWithName(ctx, defaultFuncName) 67 | } 68 | -------------------------------------------------------------------------------- /tfstate/funcs_test.go: -------------------------------------------------------------------------------- 1 | package tfstate_test 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/fujiwara/tfstate-lookup/tfstate" 8 | ) 9 | 10 | func TestMustFuncMap(t *testing.T) { 11 | ctx := context.Background() 12 | s, err := tfstate.ReadURL(ctx, "./test/terraform.tfstate") 13 | if err != nil { 14 | t.Fatal(err) 15 | } 16 | funcMap := s.FuncMapWithName(ctx, "myfunc") 17 | fn := funcMap["myfunc"].(func(string) string) 18 | if fn == nil { 19 | t.Error("no function") 20 | } 21 | if attr := fn("data.aws_caller_identity.current.account_id"); attr != "123456789012" { 22 | t.Errorf("unexpected account_id: %s", attr) 23 | } 24 | if attr := fn("module.logs.aws_cloudwatch_log_group.main['app'].retention_in_days"); attr != "30" { 25 | t.Errorf("unexpected retention_in_days: %s", attr) 26 | } 27 | defer func() { 28 | err := recover() 29 | if err == nil { 30 | t.Error("must be panic") 31 | } 32 | }() 33 | fn("data.aws_caller_identity.current.xxx") 34 | } 35 | 36 | func TestMustFuncMapF(t *testing.T) { 37 | funcMap := tfstate.MustFuncMapWithName(context.Background(), "myfunc", "./test/terraform.tfstate") 38 | fn := funcMap["myfuncf"].(func(string, ...interface{}) string) 39 | if fn == nil { 40 | t.Error("no function") 41 | } 42 | if attr := fn("data.aws_caller_identity.current.account_id"); attr != "123456789012" { 43 | t.Errorf("unexpected account_id: %s", attr) 44 | } 45 | if attr := fn("module.logs.aws_cloudwatch_log_group.main['%s'].retention_in_days", "app"); attr != "30" { 46 | t.Errorf("unexpected retention_in_days: %s", attr) 47 | } 48 | if attr := fn("aws_iam_role_policy_attachment.ec2[%d].id", 0); attr != "ec2-20190801065413533200000002" { 49 | t.Errorf("unexpected id %s", attr) 50 | } 51 | if attr := fn(`aws_iam_user.users["%s.%s"].id`, "foo", "bar"); attr != "foo.bar" { 52 | t.Errorf("unexpected user foo.bar id %s", attr) 53 | } 54 | defer func() { 55 | err := recover() 56 | if err == nil { 57 | t.Error("must be panic") 58 | } 59 | }() 60 | fn("data.aws_caller_identity.current.xxx") 61 | } 62 | -------------------------------------------------------------------------------- /tfstate/jsonnet.go: -------------------------------------------------------------------------------- 1 | package tfstate 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/google/go-jsonnet" 8 | "github.com/google/go-jsonnet/ast" 9 | ) 10 | 11 | // JsonnetNativeFuncs provides the native functions for go-jsonnet. 12 | func JsonnetNativeFuncs(ctx context.Context, prefix, stateLoc string) ([]*jsonnet.NativeFunction, error) { 13 | state, err := ReadURL(ctx, stateLoc) 14 | if err != nil { 15 | return nil, fmt.Errorf("failed to read tfstate: %s %w", stateLoc, err) 16 | } 17 | return state.JsonnetNativeFuncsWithPrefix(ctx, prefix), nil 18 | } 19 | 20 | // TFState provides a tfstate. 21 | func (s *TFState) JsonnetNativeFuncs(ctx context.Context) []*jsonnet.NativeFunction { 22 | return s.JsonnetNativeFuncsWithPrefix(ctx, "") 23 | } 24 | 25 | // JsonnetNativeFuncsWithPrefix provides the native functions for go-jsonnet with prefix. 26 | func (s *TFState) JsonnetNativeFuncsWithPrefix(ctx context.Context, prefix string) []*jsonnet.NativeFunction { 27 | return []*jsonnet.NativeFunction{ 28 | { 29 | Name: prefix + "tfstate", 30 | Params: []ast.Identifier{"address"}, 31 | Func: func(args []interface{}) (interface{}, error) { 32 | if len(args) != 1 { 33 | return nil, fmt.Errorf("tfstate expects 1 argument") 34 | } 35 | addr, ok := args[0].(string) 36 | if !ok { 37 | return nil, fmt.Errorf("tfstate expects string argument") 38 | } 39 | attrs, err := s.Lookup(addr) 40 | if err != nil { 41 | return nil, fmt.Errorf("failed to lookup %s in tfstate: %w", addr, err) 42 | } 43 | if attrs.Value == nil { 44 | return nil, fmt.Errorf("%s is not found in tfstate", addr) 45 | } 46 | return attrs.Value, nil 47 | }, 48 | }, 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tfstate/jsonnet_test.go: -------------------------------------------------------------------------------- 1 | package tfstate_test 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "encoding/json" 7 | "testing" 8 | 9 | "github.com/fujiwara/tfstate-lookup/tfstate" 10 | "github.com/google/go-cmp/cmp" 11 | "github.com/google/go-jsonnet" 12 | ) 13 | 14 | func TestJsonnetNativeFunc(t *testing.T) { 15 | ctx := context.Background() 16 | funcs, err := tfstate.JsonnetNativeFuncs(ctx, "myfunc_", "./test/terraform.tfstate") 17 | if err != nil { 18 | t.Fatal(err) 19 | } 20 | vm := jsonnet.MakeVM() 21 | for _, fn := range funcs { 22 | vm.NativeFunction(fn) 23 | } 24 | out, err := vm.EvaluateAnonymousSnippet("test.jsonnet", ` 25 | local tfstate = std.native("myfunc_tfstate"); 26 | { 27 | arn: tfstate("aws_acm_certificate.main.arn"), // string 28 | subject_alternative_names: tfstate("aws_acm_certificate.main.subject_alternative_names"), // array 29 | subject_alternative_names_0: tfstate("aws_acm_certificate.main.subject_alternative_names[0]"), // string 30 | tags: tfstate("aws_acm_certificate.main.tags"), // object 31 | tags_env: tfstate("aws_acm_certificate.main.tags").env, // string 32 | }`+"\n") 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | ob := new(bytes.Buffer) 37 | if err := json.Indent(ob, []byte(out), "", " "); err != nil { 38 | t.Fatal(err) 39 | } 40 | eb := new(bytes.Buffer) 41 | expect := `{ 42 | "arn": "arn:aws:acm:ap-northeast-1:123456789012:certificate/4986a36e-7027-4265-864b-1fe32f96d774", 43 | "subject_alternative_names": ["*.example.com"], 44 | "subject_alternative_names_0": "*.example.com", 45 | "tags": { 46 | "env": "world" 47 | }, 48 | "tags_env": "world" 49 | }` + "\n" 50 | if err := json.Indent(eb, []byte(expect), "", " "); err != nil { 51 | t.Fatal(err) 52 | } 53 | if diff := cmp.Diff(ob.String(), eb.String()); diff != "" { 54 | t.Errorf("unexpected output: %s", diff) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tfstate/lookup.go: -------------------------------------------------------------------------------- 1 | package tfstate 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "io" 8 | "net/url" 9 | "os" 10 | "path/filepath" 11 | "regexp" 12 | "sort" 13 | "strings" 14 | "sync" 15 | 16 | "github.com/itchyny/gojq" 17 | ) 18 | 19 | const ( 20 | StateVersion = 4 21 | ) 22 | 23 | var ( 24 | defaultWorkspace = "default" 25 | defaultWorkspaceKeyPrefix = "env:" 26 | ) 27 | 28 | type Object struct { 29 | Value interface{} 30 | } 31 | 32 | func (a *Object) MarshalJSON() ([]byte, error) { 33 | return json.Marshal(a.Value) 34 | } 35 | 36 | func (a *Object) Bytes() []byte { 37 | switch v := (a.Value).(type) { 38 | case string: 39 | return []byte(v) 40 | default: 41 | b, _ := json.Marshal(v) 42 | return b 43 | } 44 | } 45 | 46 | func (a *Object) String() string { 47 | return string(a.Bytes()) 48 | } 49 | 50 | // Query queries object by go-jq 51 | func (a *Object) Query(query string) (*Object, error) { 52 | jq, err := gojq.Parse(query) 53 | if err != nil { 54 | return nil, err 55 | } 56 | iter := jq.Run(a.Value) 57 | for { 58 | v, ok := iter.Next() 59 | if !ok { 60 | break 61 | } 62 | if err, ok := v.(error); ok { 63 | return nil, err 64 | } 65 | return &Object{v}, nil 66 | } 67 | return nil, fmt.Errorf("%s is not found in the state", query) 68 | } 69 | 70 | // TFState represents a tfstate 71 | type TFState struct { 72 | state tfstate 73 | scanned map[string]instance 74 | once sync.Once 75 | } 76 | 77 | type tfstate struct { 78 | Resources []resource `json:"resources"` 79 | Outputs map[string]interface{} `json:"outputs"` 80 | Backend *backend `json:"backend"` 81 | Version int `json:"version"` 82 | TerraformVersion string `json:"terraform_version"` 83 | Serial int `json:"serial"` 84 | Lineage string `json:"lineage"` 85 | } 86 | 87 | func outputValue(v interface{}) interface{} { 88 | if mv, ok := v.(map[string]interface{}); ok { 89 | if mv["value"] != nil && mv["type"] != nil { 90 | return mv["value"] 91 | } 92 | } 93 | return v 94 | } 95 | 96 | type backend struct { 97 | Type string `json:"type"` 98 | Config map[string]interface{} 99 | } 100 | 101 | type resource struct { 102 | Module string `json:"module"` 103 | Mode string `json:"mode"` 104 | Type string `json:"type"` 105 | Name string `json:"name"` 106 | Each string `json:"each"` 107 | Provider string `json:"provider"` 108 | Instances instances `json:"instances"` 109 | } 110 | 111 | type instances []instance 112 | 113 | type instance struct { 114 | IndexKey json.RawMessage `json:"index_key"` 115 | SchemaVersion int `json:"schema_version"` 116 | Attributes interface{} `json:"attributes"` 117 | AttributesFlat interface{} `json:"attributes_flat"` 118 | Private string `json:"private"` 119 | 120 | data interface{} 121 | } 122 | 123 | // Read reads a tfstate from io.Reader 124 | func Read(ctx context.Context, src io.Reader) (*TFState, error) { 125 | return ReadWithWorkspace(ctx, src, defaultWorkspace) 126 | } 127 | 128 | // ReadWithWorkspace reads a tfstate from io.Reader with workspace 129 | func ReadWithWorkspace(ctx context.Context, src io.Reader, ws string) (*TFState, error) { 130 | if ws == "" { 131 | ws = defaultWorkspace 132 | } 133 | var s TFState 134 | if err := json.NewDecoder(src).Decode(&s.state); err != nil { 135 | return nil, fmt.Errorf("invalid json: %w", err) 136 | } 137 | if s.state.Backend != nil { 138 | remote, err := readRemoteState(ctx, s.state.Backend, ws) 139 | if err != nil { 140 | return nil, err 141 | } 142 | defer remote.Close() 143 | return Read(ctx, remote) 144 | } 145 | if s.state.Version != StateVersion { 146 | return nil, fmt.Errorf("unsupported state version %d", s.state.Version) 147 | } 148 | return &s, nil 149 | } 150 | 151 | // ReadFile reads terraform.tfstate from the file 152 | // (Firstly, a workspace reads TF_WORKSPACE environment variable. if it doesn't exist, it reads from environment file in the same directory) 153 | func ReadFile(ctx context.Context, file string) (*TFState, error) { 154 | ws := func() string { 155 | if env := os.Getenv("TF_WORKSPACE"); env != "" { 156 | return env 157 | } 158 | f, _ := os.ReadFile(filepath.Join(filepath.Dir(file), "environment")) 159 | return string(f) 160 | 161 | }() 162 | f, err := os.Open(file) 163 | if err != nil { 164 | return nil, fmt.Errorf("failed to read tfstate from %s: %w", file, err) 165 | } 166 | defer f.Close() 167 | return ReadWithWorkspace(ctx, f, ws) 168 | } 169 | 170 | // ReadURL reads terraform.tfstate from the URL. 171 | func ReadURL(ctx context.Context, loc string) (*TFState, error) { 172 | u, err := url.Parse(loc) 173 | if err != nil { 174 | return nil, err 175 | } 176 | 177 | var src io.ReadCloser 178 | switch u.Scheme { 179 | case "http", "https": 180 | src, err = readHTTP(ctx, u.String()) 181 | case "s3": 182 | key := strings.TrimPrefix(u.Path, "/") 183 | src, err = readS3(ctx, u.Host, key, S3Option{ 184 | Endpoint: os.Getenv(S3EndpointEnvKey), 185 | }) 186 | case "gs": 187 | key := strings.TrimPrefix(u.Path, "/") 188 | src, err = readGCS(ctx, u.Host, key, "", os.Getenv("GOOGLE_ENCRYPTION_KEY")) 189 | case "azurerm": 190 | split := strings.SplitN(u.Path, "/", 4) 191 | 192 | if len(split) < 4 { 193 | err = fmt.Errorf("invalid azurerm url: %s", u.String()) 194 | break 195 | } 196 | 197 | src, err = readAzureRM(ctx, u.Host, split[1], split[2], split[3], azureRMOption{subscriptionID: u.User.Username()}) 198 | case "file": 199 | src, err = os.Open(u.Path) 200 | case "remote": 201 | split := strings.Split(u.Path, "/") 202 | src, err = readTFE(ctx, u.Host, split[1], split[2], os.Getenv("TFE_TOKEN")) 203 | case "": 204 | return ReadFile(ctx, u.Path) 205 | default: 206 | err = fmt.Errorf("URL scheme %s is not supported", u.Scheme) 207 | } 208 | if err != nil { 209 | return nil, fmt.Errorf("failed to read tfstate from %s: %w", u.String(), err) 210 | } 211 | defer src.Close() 212 | return Read(ctx, src) 213 | } 214 | 215 | // Lookup lookups attributes of the specified key in tfstate 216 | func (s *TFState) Lookup(key string) (*Object, error) { 217 | s.once.Do(s.scan) 218 | var found instance 219 | var foundName string 220 | for name, ins := range s.scanned { 221 | if strings.HasPrefix(key, name) { 222 | // longest match 223 | if len(foundName) < len(name) { 224 | found = ins 225 | foundName = name 226 | } 227 | } 228 | } 229 | if foundName == "" { 230 | return &Object{}, nil 231 | } 232 | 233 | query := strings.TrimPrefix(key, foundName) 234 | if query == "" { 235 | query = "." // empty query means the whole object 236 | } 237 | if strings.HasPrefix(query, "[") { // e.g. output.foo[0] 238 | query = "." + query 239 | } 240 | if strings.HasPrefix(query, ".") || query == "" { 241 | attr := &Object{noneNil(found.data, found.Attributes, found.AttributesFlat)} 242 | return attr.Query(quoteJQQuery(query)) 243 | } 244 | 245 | return &Object{}, nil 246 | } 247 | 248 | // query is passed to gojq.Compile() such as `.outputs.arn`. 249 | // If query contains the characters other than [jq's identifier-like characters](https://stedolan.github.io/jq/manual/#ObjectIdentifier-Index:.foo,.foo.bar), 250 | // we must quote them like `.outputs["repository-arn"]`. 251 | // 252 | // quoteJQQuery does it. 253 | var ( 254 | quoteSplitRegex = regexp.MustCompile(`[.\[\]]`) 255 | quoteIndexRegex = regexp.MustCompile(`^-?[0-9]+$`) 256 | ) 257 | 258 | func quoteJQQuery(query string) string { 259 | if query == "" || !strings.Contains(query, "-") { 260 | // short-circuit if query is empty or doesn't contain hyphen 261 | return query 262 | } 263 | parts := quoteSplitRegex.Split(query, -1) 264 | var builder strings.Builder 265 | builder.Grow(len(query) + 5*len(parts)) 266 | builder.WriteByte('.') 267 | for _, part := range parts { 268 | if part == "" { 269 | continue 270 | } 271 | builder.WriteByte('[') 272 | if quoteIndexRegex.MatchString(part) { 273 | builder.WriteString(part) 274 | } else { 275 | if !strings.HasPrefix(part, `"`) { 276 | builder.WriteByte('"') 277 | } 278 | builder.WriteString(part) 279 | if !strings.HasSuffix(part, `"`) { 280 | builder.WriteByte('"') 281 | } 282 | } 283 | builder.WriteByte(']') 284 | } 285 | return builder.String() 286 | } 287 | 288 | // List lists resource and output names in tfstate 289 | func (s *TFState) List() ([]string, error) { 290 | s.once.Do(s.scan) 291 | names := make([]string, 0, len(s.scanned)) 292 | for key := range s.scanned { 293 | names = append(names, key) 294 | } 295 | sort.Strings(names) 296 | return names, nil 297 | } 298 | 299 | // Dump dumps all resources, outputs, and data sources in tfstate 300 | func (s *TFState) Dump() (map[string]*Object, error) { 301 | s.once.Do(s.scan) 302 | res := make(map[string]*Object, len(s.scanned)) 303 | for key, ins := range s.scanned { 304 | res[key] = &Object{noneNil(ins.data, ins.Attributes, ins.AttributesFlat)} 305 | } 306 | return res, nil 307 | } 308 | 309 | func (s *TFState) scan() { 310 | s.scanned = make(map[string]instance, len(s.state.Resources)) 311 | for key, value := range s.state.Outputs { 312 | s.scanned["output."+key] = instance{data: outputValue(value)} 313 | } 314 | for _, r := range s.state.Resources { 315 | var module string 316 | if r.Module != "" { 317 | module = r.Module + "." 318 | } 319 | switch r.Mode { 320 | case "data", "managed": 321 | prefix := "" 322 | if r.Mode == "data" { 323 | prefix = "data." 324 | } 325 | if r.Mode == "data" && r.Type == "terraform_remote_state" { 326 | if a, ok := r.Instances[0].Attributes.(map[string]interface{}); ok { 327 | data := make(map[string]interface{}, len(a)) 328 | for k, v := range a { 329 | data[k] = outputValue(v) 330 | } 331 | key := module + fmt.Sprintf("%s%s.%s", prefix, r.Type, r.Name) 332 | s.scanned[key] = instance{data: data} 333 | } 334 | } else { 335 | for _, i := range r.Instances { 336 | ins := i 337 | var key string 338 | if len(ins.IndexKey) == 0 { 339 | key = module + fmt.Sprintf("%s%s.%s", prefix, r.Type, r.Name) 340 | } else { 341 | key = module + fmt.Sprintf("%s%s.%s[%s]", prefix, r.Type, r.Name, string(i.IndexKey)) 342 | } 343 | s.scanned[key] = ins 344 | } 345 | } 346 | } 347 | } 348 | } 349 | 350 | func noneNil(args ...interface{}) interface{} { 351 | for _, v := range args { 352 | if v != nil { 353 | return v 354 | } 355 | } 356 | return nil 357 | } 358 | -------------------------------------------------------------------------------- /tfstate/lookup_test.go: -------------------------------------------------------------------------------- 1 | package tfstate_test 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net/http" 7 | "net/http/httptest" 8 | "os" 9 | "sort" 10 | "testing" 11 | 12 | "github.com/fujiwara/tfstate-lookup/tfstate" 13 | "github.com/google/go-cmp/cmp" 14 | ) 15 | 16 | type TestSuite struct { 17 | Key string 18 | Result interface{} 19 | } 20 | 21 | func init() { 22 | testing.Init() 23 | } 24 | 25 | var TestNames = []string{ 26 | `output.bar`, 27 | `output.foo`, 28 | `output.dash-tuple`, 29 | `data.aws_caller_identity.current`, 30 | `aws_acm_certificate.main`, 31 | `module.logs.aws_cloudwatch_log_group.main`, 32 | `module.logs.aws_cloudwatch_log_group.main["app"]`, 33 | `module.logs.aws_cloudwatch_log_group.main["web"]`, 34 | `aws_iam_role_policy_attachment.ec2[0]`, 35 | `aws_iam_role_policy_attachment.ec2[1]`, 36 | `module.webapp.module.ecs_task_roles.aws_iam_role.task_execution_role`, 37 | `module.subnets.aws_subnet.main[0]`, 38 | `module.subnets.aws_subnet.main[1]`, 39 | `aws_iam_user.user["me"]`, 40 | `aws_iam_user.users["foo.bar"]`, 41 | `aws_iam_user.users["hoge.fuga"]`, 42 | `data.aws_lb_target_group.app["dev1"]`, 43 | `data.aws_lb_target_group.app["dev2"]`, 44 | `data.aws_lb_target_group.app["dev3"]`, 45 | `data.terraform_remote_state.remote`, 46 | `data.terraform_remote_state.hyphenated-id`, 47 | } 48 | 49 | var TestSuitesOK = []TestSuite{ 50 | { 51 | Key: "data.aws_caller_identity.current.account_id", 52 | Result: "123456789012", 53 | }, 54 | { 55 | Key: "data.aws_caller_identity.xxxx.account_id", 56 | Result: nil, 57 | }, 58 | { 59 | Key: "aws_acm_certificate.main.validation_method", 60 | Result: "DNS", 61 | }, 62 | { 63 | Key: "aws_acm_certificate.main2", 64 | Result: nil, 65 | }, 66 | { 67 | Key: "aws_acm_certificate.main.validation_method_xxx", 68 | Result: nil, 69 | }, 70 | { 71 | Key: "aws_acm_certificate.main.subject_alternative_names", 72 | Result: []interface{}{string("*.example.com")}, 73 | }, 74 | { 75 | Key: "aws_acm_certificate.main.subject_alternative_names[0]", 76 | Result: "*.example.com", 77 | }, 78 | { 79 | Key: "aws_acm_certificate.main.subject_alternative_names[2]", 80 | Result: nil, 81 | }, 82 | { 83 | Key: `module.logs.aws_cloudwatch_log_group.main["vanish"]`, 84 | Result: nil, 85 | }, 86 | { 87 | Key: `module.logs.aws_cloudwatch_log_group.main["app"].id`, 88 | Result: "/main/app", 89 | }, 90 | { 91 | Key: `module.xxx.aws_cloudwatch_log_group.main["app"].id`, 92 | Result: nil, 93 | }, 94 | { 95 | Key: `module.logs.aws_cloudwatch_log_group.main["app"].retention_in_days`, 96 | Result: float64(30), 97 | }, 98 | { 99 | Key: `module.logs.aws_cloudwatch_log_group.main["app"].retention_in_days_xxx`, 100 | Result: nil, 101 | }, 102 | { 103 | Key: `module.logs.aws_cloudwatch_log_group.main.name`, 104 | Result: "/main/vanish", 105 | }, 106 | { 107 | Key: `module.logs.aws_cloudwatch_log_group.ma`, 108 | Result: nil, 109 | }, 110 | { 111 | Key: `aws_iam_role_policy_attachment.ec2[1].id`, 112 | Result: "ec2-20190801065413531100000001", 113 | }, 114 | { 115 | Key: `aws_iam_role_policy_attachment.ec2[2].id`, 116 | Result: nil, 117 | }, 118 | { 119 | Key: `output.foo`, 120 | Result: "FOO", 121 | }, 122 | { 123 | Key: `output.bar[1]`, 124 | Result: "B", 125 | }, 126 | { 127 | Key: `output.baz`, 128 | Result: nil, 129 | }, 130 | { 131 | Key: "module.webapp.module.ecs_task_roles.aws_iam_role.task_execution_role.name", 132 | Result: "task-execution-role", 133 | }, 134 | { 135 | Key: "module.webapp.xxxx.ecs_task_roles.aws_iam_role.task_execution_role", 136 | Result: nil, 137 | }, 138 | { 139 | Key: "xxxx.webapp.module.ecs_task_roles.aws_iam_role.task_execution_role", 140 | Result: nil, 141 | }, 142 | { 143 | Key: "module.subnets.aws_subnet.main[0].cidr_block", 144 | Result: "10.11.12.0/22", 145 | }, 146 | { 147 | Key: "module.subnets.aws_subnet.main[1].cidr_block", 148 | Result: "10.11.15.0/22", 149 | }, 150 | { 151 | Key: `aws_iam_user.users["foo.bar"].name`, 152 | Result: "foo.bar", 153 | }, 154 | { 155 | Key: `aws_iam_user.users["hoge.fuga"].name`, 156 | Result: "hoge.fuga", 157 | }, 158 | { 159 | Key: `data.aws_lb_target_group.app["dev1"].name`, 160 | Result: "dev-dev1-app", 161 | }, 162 | { 163 | Key: `module.example.aws_vpc.example`, 164 | Result: nil, 165 | }, 166 | { 167 | Key: `data.terraform_remote_state.remote.outputs.kms_key.arn`, 168 | Result: `arn:aws:kms:ap-northeast-1:123456789012:key/500193e3-ddd9-4581-ab0c-fd7aeaedf3e1`, 169 | }, 170 | { 171 | Key: `data.terraform_remote_state.remote.outputs.kms_key_arn`, 172 | Result: `arn:aws:kms:ap-northeast-1:123456789012:key/500193e3-ddd9-4581-ab0c-fd7aeaedf3e1`, 173 | }, 174 | { 175 | Key: `data.terraform_remote_state.remote.outputs.mylist[1]`, 176 | Result: float64(2), 177 | }, 178 | { 179 | Key: `aws_iam_user.user["me"].arn`, 180 | Result: `arn:aws:iam::xxxxxxxxxxxx:user/me`, 181 | }, 182 | { 183 | Key: `data.terraform_remote_state.hyphenated-id.outputs.repository-uri`, 184 | Result: `123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/app`, 185 | }, 186 | { 187 | Key: `output.dash-tuple[1]`, 188 | Result: float64(2), 189 | }, 190 | { 191 | Key: `output.dash-tuple[-1]`, 192 | Result: float64(1), 193 | }, 194 | } 195 | 196 | func testLookupState(t *testing.T, state *tfstate.TFState) { 197 | for _, ts := range TestSuitesOK { 198 | t.Run(ts.Key, func(t *testing.T) { 199 | res, err := state.Lookup(ts.Key) 200 | if err != nil { 201 | t.Error(err) 202 | } 203 | t.Log(ts.Key, res) 204 | if diff := cmp.Diff(res.Value, ts.Result); diff != "" { 205 | t.Errorf("%s unexpected result %s", ts.Key, diff) 206 | } 207 | }) 208 | } 209 | } 210 | 211 | func TestLookupFile(t *testing.T) { 212 | f, err := os.Open("test/terraform.tfstate") 213 | if err != nil { 214 | t.Error(err) 215 | } 216 | state, err := tfstate.Read(context.Background(), f) 217 | if err != nil { 218 | t.Error(err) 219 | } 220 | testLookupState(t, state) 221 | } 222 | 223 | func TestLookupFileURL(t *testing.T) { 224 | d, _ := os.Getwd() 225 | state, err := tfstate.ReadURL(context.Background(), fmt.Sprintf("file://%s/test/terraform.tfstate", d)) 226 | if err != nil { 227 | t.Error(err) 228 | } 229 | testLookupState(t, state) 230 | } 231 | 232 | func BenchmarkLookupFile(b *testing.B) { 233 | f, err := os.Open("test/terraform.tfstate") 234 | if err != nil { 235 | b.Error(err) 236 | } 237 | state, err := tfstate.Read(context.Background(), f) 238 | if err != nil { 239 | b.Error(err) 240 | } 241 | for i := 0; i < b.N; i++ { 242 | for _, ts := range TestSuitesOK { 243 | _, err := state.Lookup(ts.Key) 244 | if err != nil { 245 | b.Error(err) 246 | } 247 | } 248 | } 249 | } 250 | 251 | func TestLookupHTTPURL(t *testing.T) { 252 | h := http.FileServer(http.Dir(".")) 253 | ts := httptest.NewServer(h) 254 | defer ts.Close() 255 | t.Logf("testing URL %s", ts.URL) 256 | state, err := tfstate.ReadURL(context.Background(), ts.URL+"/test/terraform.tfstate") 257 | if err != nil { 258 | t.Error(err) 259 | } 260 | testLookupState(t, state) 261 | } 262 | 263 | func TestList(t *testing.T) { 264 | f, err := os.Open("test/terraform.tfstate") 265 | if err != nil { 266 | t.Error(err) 267 | } 268 | state, err := tfstate.Read(context.Background(), f) 269 | if err != nil { 270 | t.Error(err) 271 | } 272 | names, err := state.List() 273 | if err != nil { 274 | t.Error(err) 275 | } 276 | sort.Strings(names) 277 | sort.Strings(TestNames) 278 | if diff := cmp.Diff(names, TestNames); diff != "" { 279 | t.Errorf("unexpected list names %s", diff) 280 | } 281 | } 282 | 283 | func TestDump(t *testing.T) { 284 | f, err := os.Open("test/terraform.tfstate") 285 | if err != nil { 286 | t.Error(err) 287 | } 288 | state, err := tfstate.Read(context.Background(), f) 289 | if err != nil { 290 | t.Error(err) 291 | } 292 | dump, _ := state.Dump() 293 | if len(dump) != len(TestNames) { 294 | t.Errorf("unexpected dump length %d", len(dump)) 295 | } 296 | 297 | t.Run("compare dump keys with List()", func(t *testing.T) { 298 | dumpKeys := make([]string, 0, len(dump)) 299 | for key := range dump { 300 | dumpKeys = append(dumpKeys, key) 301 | } 302 | listKeys, _ := state.List() 303 | sort.Strings(dumpKeys) 304 | sort.Strings(listKeys) 305 | if diff := cmp.Diff(dumpKeys, listKeys); diff != "" { 306 | t.Errorf("unexpected dump keys %s", diff) 307 | } 308 | }) 309 | 310 | t.Run("compare dump values with Lookup()", func(t *testing.T) { 311 | for key, dv := range dump { 312 | lv, err := state.Lookup(key) 313 | if err != nil { 314 | t.Error(err) 315 | } 316 | if diff := cmp.Diff(dv, lv); diff != "" { 317 | t.Errorf("unexpected dump value %s", diff) 318 | } 319 | } 320 | }) 321 | } 322 | -------------------------------------------------------------------------------- /tfstate/remote.go: -------------------------------------------------------------------------------- 1 | package tfstate 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "io" 7 | ) 8 | 9 | func strp(v interface{}) *string { 10 | if v == nil { 11 | return nil 12 | } 13 | if vs, ok := v.(string); ok { 14 | return &vs 15 | } 16 | return nil 17 | } 18 | 19 | func strpe(v interface{}) *string { 20 | empty := "" 21 | if v == nil { 22 | return &empty 23 | } 24 | if vs, ok := v.(string); ok { 25 | return &vs 26 | } 27 | return &empty 28 | } 29 | 30 | func readRemoteState(ctx context.Context, b *backend, ws string) (io.ReadCloser, error) { 31 | switch b.Type { 32 | case "gcs": 33 | return readGCSState(ctx, b.Config, ws) 34 | case "azurerm": 35 | return readAzureRMState(ctx, b.Config, ws) 36 | case "s3": 37 | return readS3State(ctx, b.Config, ws) 38 | case "remote": 39 | return readTFEState(ctx, b.Config, ws) 40 | default: 41 | return nil, fmt.Errorf("backend type %s is not supported", b.Type) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tfstate/remote_azurerm.go: -------------------------------------------------------------------------------- 1 | package tfstate 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "io" 7 | "os" 8 | 9 | "github.com/Azure/azure-sdk-for-go/sdk/azidentity" 10 | "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage" 11 | "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob" 12 | "github.com/Azure/go-autorest/autorest/azure/cli" 13 | ) 14 | 15 | type azureRMOption struct { 16 | accessKey string 17 | useAzureAdAuth string 18 | subscriptionID string 19 | } 20 | 21 | func readAzureRMState(ctx context.Context, config map[string]interface{}, ws string) (io.ReadCloser, error) { 22 | accountName, containerName, key := *strp(config["storage_account_name"]), *strpe(config["container_name"]), *strpe(config["key"]) 23 | resourceGroupName := *strp(config["resource_group_name"]) 24 | if ws != defaultWorkspace { 25 | if prefix := strp(config["workspace_key_prefix"]); prefix != nil { 26 | key = key + *prefix + ws 27 | } else { 28 | key = key + defaultWorkspaceKeyPrefix + ws 29 | } 30 | } 31 | opt := azureRMOption{ 32 | accessKey: *strpe(config["access_key"]), 33 | useAzureAdAuth: *strpe(config["use_azuread_auth"]), 34 | subscriptionID: *strpe(config["subscription_id"]), 35 | } 36 | return readAzureRM(ctx, resourceGroupName, accountName, containerName, key, opt) 37 | } 38 | 39 | func readAzureRM(ctx context.Context, resourceGroupName string, accountName string, containerName string, key string, opt azureRMOption) (io.ReadCloser, error) { 40 | serviceUrl := fmt.Sprintf("https://%s.blob.core.windows.net/", accountName) 41 | 42 | var client *azblob.Client 43 | 44 | if opt.useAzureAdAuth == "true" || os.Getenv("ARM_USE_AZUREAD") == "true" { 45 | cred, err := getDefaultAzureCredential() 46 | if err != nil { 47 | return nil, err 48 | } 49 | 50 | client, err = azblob.NewClient(serviceUrl, cred, nil) 51 | if err != nil { 52 | return nil, fmt.Errorf("failed to setup client: %w", err) 53 | } 54 | } else { 55 | // get blob access key 56 | var accountKey string 57 | for _, gen := range []func() (string, error){ 58 | func() (string, error) { return opt.accessKey, nil }, 59 | func() (string, error) { return os.Getenv("AZURE_STORAGE_ACCESS_KEY"), nil }, 60 | func() (string, error) { return getDefaultAzureAccessKey(ctx, resourceGroupName, accountName, opt) }, 61 | } { 62 | key, err := gen() 63 | if err != nil { 64 | return nil, err 65 | } else if key != "" { 66 | accountKey = key 67 | break 68 | } 69 | } 70 | if accountKey == "" { 71 | return nil, fmt.Errorf("Blob access key not found in ENV, terraform config and can't be fetched from current Azure Profile") 72 | } 73 | 74 | // Authenticate 75 | credential, err := azblob.NewSharedKeyCredential(accountName, accountKey) 76 | if err != nil { 77 | return nil, fmt.Errorf("failed to create credential: %w", err) 78 | } 79 | 80 | client, err = azblob.NewClientWithSharedKeyCredential(serviceUrl, credential, nil) 81 | if err != nil { 82 | return nil, fmt.Errorf("failed to setup client: %w", err) 83 | } 84 | } 85 | 86 | blobDownloadResponse, err := client.DownloadStream(ctx, containerName, key, nil) 87 | if err != nil { 88 | return nil, fmt.Errorf("failed to download blob: %w", err) 89 | } 90 | 91 | r := blobDownloadResponse.Body 92 | return r, nil 93 | } 94 | 95 | func getDefaultAzureSubscription() (string, error) { 96 | if value, ok := os.LookupEnv("AZURE_SUBSCRIPTION_ID"); ok { 97 | return value, nil 98 | } 99 | 100 | profilePath, _ := cli.ProfilePath() 101 | profile, err := cli.LoadProfile(profilePath) 102 | if err != nil { 103 | return "", fmt.Errorf("failed to load profile: %w", err) 104 | } 105 | subscriptionID := "" 106 | for _, x := range profile.Subscriptions { 107 | if !x.IsDefault { 108 | continue 109 | } 110 | subscriptionID = x.ID 111 | } 112 | return subscriptionID, nil 113 | } 114 | 115 | func getDefaultAzureAccessKey(ctx context.Context, resourceGroupName string, accountName string, opt azureRMOption) (string, error) { 116 | cred, err := getDefaultAzureCredential() 117 | if err != nil { 118 | return "", err 119 | } 120 | 121 | subscriptionID, err := getAzureSubscription(opt) 122 | if err != nil { 123 | return "", err 124 | } 125 | 126 | clientFactory, err := armstorage.NewClientFactory(subscriptionID, cred, nil) 127 | if err != nil { 128 | return "", fmt.Errorf("failed to create client factory: %w", err) 129 | } 130 | keys, err := clientFactory.NewAccountsClient().ListKeys(ctx, resourceGroupName, accountName, nil) 131 | if err != nil { 132 | return "", fmt.Errorf("failed to list keys: %w", err) 133 | } 134 | 135 | return *keys.Keys[0].Value, nil 136 | } 137 | 138 | func getAzureSubscription(opt azureRMOption) (string, error) { 139 | if opt.subscriptionID != "" { 140 | return opt.subscriptionID, nil 141 | } 142 | 143 | subscriptionID, err := getDefaultAzureSubscription() 144 | if err != nil { 145 | return "", fmt.Errorf("failed to get default subscription: %w", err) 146 | } 147 | 148 | return subscriptionID, nil 149 | } 150 | 151 | func getDefaultAzureCredential() (*azidentity.DefaultAzureCredential, error) { 152 | cred, err := azidentity.NewDefaultAzureCredential(nil) 153 | if err != nil { 154 | return nil, fmt.Errorf("failed to authorize: %w", err) 155 | } 156 | return cred, nil 157 | } 158 | -------------------------------------------------------------------------------- /tfstate/remote_gcs.go: -------------------------------------------------------------------------------- 1 | package tfstate 2 | 3 | import ( 4 | "context" 5 | "encoding/base64" 6 | "io" 7 | "path" 8 | 9 | "cloud.google.com/go/storage" 10 | "google.golang.org/api/option" 11 | ) 12 | 13 | func readGCSState(ctx context.Context, config map[string]interface{}, ws string) (io.ReadCloser, error) { 14 | bucket := *strp(config["bucket"]) 15 | prefix := *strpe(config["prefix"]) 16 | credentials := *strpe(config["credentials"]) 17 | encryption_key := *strpe(config["encryption_key"]) 18 | 19 | key := path.Join(prefix, ws+".tfstate") 20 | 21 | return readGCS(ctx, bucket, key, credentials, encryption_key) 22 | } 23 | 24 | func readGCS(ctx context.Context, bucket, key, credentials, encryption_key string) (io.ReadCloser, error) { 25 | var err error 26 | 27 | var client *storage.Client 28 | if credentials != "" { 29 | client, err = storage.NewClient(ctx, option.WithCredentialsFile(credentials)) 30 | } else { 31 | client, err = storage.NewClient(ctx) 32 | } 33 | 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | bkt := client.Bucket(bucket) 39 | obj := bkt.Object(key) 40 | 41 | var r *storage.Reader 42 | 43 | if encryption_key != "" { 44 | decodedKey, _ := base64.StdEncoding.DecodeString(encryption_key) 45 | r, err = obj.Key(decodedKey).NewReader(ctx) 46 | } else { 47 | r, err = obj.NewReader(ctx) 48 | } 49 | 50 | if err != nil { 51 | return nil, err 52 | } 53 | 54 | return r, nil 55 | } 56 | -------------------------------------------------------------------------------- /tfstate/remote_http.go: -------------------------------------------------------------------------------- 1 | package tfstate 2 | 3 | import ( 4 | "context" 5 | "io" 6 | "net/http" 7 | ) 8 | 9 | func readHTTP(ctx context.Context, u string) (io.ReadCloser, error) { 10 | req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil) 11 | if err != nil { 12 | return nil, err 13 | } 14 | return readHTTPWithRequest(ctx, req) 15 | } 16 | 17 | func readHTTPWithRequest(ctx context.Context, req *http.Request) (io.ReadCloser, error) { 18 | if c := req.Context(); c != ctx { 19 | req = req.WithContext(ctx) 20 | } 21 | resp, err := http.DefaultClient.Do(req) 22 | if err != nil { 23 | return nil, err 24 | } 25 | return resp.Body, nil 26 | } 27 | -------------------------------------------------------------------------------- /tfstate/remote_s3.go: -------------------------------------------------------------------------------- 1 | package tfstate 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "io" 7 | "path" 8 | 9 | "github.com/aws/aws-sdk-go-v2/aws" 10 | "github.com/aws/aws-sdk-go-v2/aws/arn" 11 | "github.com/aws/aws-sdk-go-v2/config" 12 | "github.com/aws/aws-sdk-go-v2/credentials" 13 | "github.com/aws/aws-sdk-go-v2/credentials/stscreds" 14 | "github.com/aws/aws-sdk-go-v2/feature/s3/manager" 15 | "github.com/aws/aws-sdk-go-v2/service/s3" 16 | "github.com/aws/aws-sdk-go-v2/service/sts" 17 | ) 18 | 19 | const S3EndpointEnvKey = "AWS_ENDPOINT_URL_S3" 20 | 21 | type S3Option struct { 22 | AccessKey string 23 | SecretKey string 24 | Region string 25 | RoleArn string 26 | Endpoint string 27 | } 28 | 29 | func readS3State(ctx context.Context, config map[string]interface{}, ws string) (io.ReadCloser, error) { 30 | bucket, key := *strpe(config["bucket"]), *strpe(config["key"]) 31 | if ws != defaultWorkspace { 32 | if prefix := strp(config["workspace_key_prefix"]); prefix != nil { 33 | key = path.Join(*prefix, ws, key) 34 | } else { 35 | key = path.Join(defaultWorkspaceKeyPrefix, ws, key) 36 | } 37 | } 38 | opt := S3Option{ 39 | Region: *strpe(config["region"]), 40 | RoleArn: *strpe(config["role_arn"]), 41 | AccessKey: *strpe(config["access_key"]), 42 | SecretKey: *strpe(config["secret_key"]), 43 | } 44 | 45 | if config["endpoints"] != nil { 46 | if es, ok := config["endpoints"].(map[string]interface{}); ok { 47 | if es["s3"] != nil { 48 | opt.Endpoint = es["s3"].(string) 49 | } 50 | } 51 | } 52 | return readS3(ctx, bucket, key, opt) 53 | } 54 | 55 | func readS3(ctx context.Context, bucket, key string, opt S3Option) (io.ReadCloser, error) { 56 | var staticProvider aws.CredentialsProvider 57 | if opt.AccessKey != "" && opt.SecretKey != "" { 58 | staticProvider = credentials.NewStaticCredentialsProvider(opt.AccessKey, opt.SecretKey, "") 59 | } 60 | 61 | cfg, err := config.LoadDefaultConfig(ctx, 62 | config.WithRegion(opt.Region), 63 | config.WithCredentialsProvider(staticProvider), 64 | ) 65 | if err != nil { 66 | return nil, fmt.Errorf("failed to load config: %w", err) 67 | } 68 | region, err := getBucketRegion(ctx, cfg, bucket) 69 | if err != nil { 70 | return nil, fmt.Errorf("failed to get bucket region: %w", err) 71 | } 72 | if region != opt.Region { 73 | // reload config with bucket region 74 | cfg, err = config.LoadDefaultConfig(ctx, 75 | config.WithRegion(region), 76 | ) 77 | if err != nil { 78 | return nil, err 79 | } 80 | } 81 | if opt.RoleArn != "" { 82 | arn, err := arn.Parse(opt.RoleArn) 83 | if err != nil { 84 | return nil, err 85 | } 86 | creds := stscreds.NewAssumeRoleProvider(sts.NewFromConfig(cfg), arn.String()) 87 | cfg.Credentials = creds 88 | } 89 | s3Opts := []func(*s3.Options){} 90 | if u := opt.Endpoint; u != "" { 91 | s3Opts = append(s3Opts, func(o *s3.Options) { 92 | o.BaseEndpoint = aws.String(u) 93 | }) 94 | } 95 | svc := s3.NewFromConfig(cfg, s3Opts...) 96 | result, err := svc.GetObject(ctx, &s3.GetObjectInput{ 97 | Bucket: aws.String(bucket), 98 | Key: aws.String(key), 99 | }) 100 | if err != nil { 101 | return nil, err 102 | } 103 | return result.Body, nil 104 | } 105 | 106 | func getBucketRegion(ctx context.Context, cfg aws.Config, bucket string) (string, error) { 107 | return manager.GetBucketRegion(ctx, s3.NewFromConfig(cfg), bucket) 108 | } 109 | -------------------------------------------------------------------------------- /tfstate/remote_s3_test.go: -------------------------------------------------------------------------------- 1 | package tfstate_test 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "testing" 7 | 8 | "github.com/fujiwara/tfstate-lookup/tfstate" 9 | ) 10 | 11 | var testBuckets = []struct { 12 | bucket string 13 | region string 14 | }{ 15 | { 16 | bucket: "tfstate-lookup", 17 | region: "us-east-1", 18 | }, 19 | { 20 | bucket: "tfstate-lookup-oregon", 21 | region: "us-west-2", 22 | }, 23 | } 24 | 25 | func TestBucketRegion(t *testing.T) { 26 | t.Setenv("AWS_ACCESS_KEY_ID", "DUMMY") // s3/manager.GetBucketRegion requires credentials 27 | t.Setenv("AWS_SECRET_ACCESS_KEY", "DUMMY") 28 | for _, b := range testBuckets { 29 | region, err := tfstate.GetBucketRegion(b.bucket) 30 | if err != nil { 31 | t.Error(err) 32 | } 33 | if b.region != region { 34 | t.Errorf("unexpected region of %s. expected %s, got %s", b.bucket, b.region, region) 35 | } 36 | } 37 | } 38 | 39 | func TestReadS3(t *testing.T) { 40 | envKey := "TEST_" + tfstate.S3EndpointEnvKey 41 | endpoint := os.Getenv(envKey) 42 | if endpoint == "" { 43 | t.Skipf("%s is not set", envKey) 44 | } 45 | t.Setenv(tfstate.S3EndpointEnvKey, endpoint) 46 | 47 | ctx := context.TODO() // use t.Context() in Go 1.24 48 | _, err := tfstate.ReadURL(ctx, "s3://mybucket/terraform.tfstate") 49 | if err != nil { 50 | t.Error("failed to read s3", err) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tfstate/remote_tfe.go: -------------------------------------------------------------------------------- 1 | package tfstate 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | "os" 9 | 10 | tfe "github.com/hashicorp/go-tfe" 11 | ) 12 | 13 | func readTFEState(ctx context.Context, config map[string]interface{}, ws string) (io.ReadCloser, error) { 14 | hostname, organization, token := *strpe(config["hostname"]), *strp(config["organization"]), *strpe(config["token"]) 15 | if token == "" { 16 | token = os.Getenv("TFE_TOKEN") 17 | } 18 | 19 | workspaces, ok := config["workspaces"].(map[string]interface{}) 20 | if !ok { 21 | return nil, fmt.Errorf("failed to parse workspaces") 22 | } 23 | 24 | name, prefix := *strpe(workspaces["name"]), *strpe(workspaces["prefix"]) 25 | if name != "" { 26 | return readTFE(ctx, hostname, organization, name, token) 27 | } 28 | 29 | if prefix != "" { 30 | return readTFE(ctx, hostname, organization, prefix+ws, token) 31 | } 32 | 33 | return nil, fmt.Errorf("workspaces requires either name or prefix") 34 | } 35 | 36 | func readTFE(ctx context.Context, hostname string, organization string, ws string, token string) (io.ReadCloser, error) { 37 | var address string 38 | address = tfe.DefaultAddress 39 | if hostname != "" { 40 | address = "https://" + hostname 41 | } 42 | 43 | var client *tfe.Client 44 | client, err := tfe.NewClient(&tfe.Config{ 45 | Address: address, 46 | Token: token, 47 | }) 48 | if err != nil { 49 | return nil, err 50 | } 51 | 52 | workspace, err := client.Workspaces.Read(ctx, organization, ws) 53 | if err != nil { 54 | return nil, err 55 | } 56 | state, err := client.StateVersions.ReadCurrent(ctx, workspace.ID) 57 | if err != nil { 58 | return nil, err 59 | } 60 | req, err := http.NewRequest(http.MethodGet, state.DownloadURL, nil) 61 | if err != nil { 62 | return nil, err 63 | } 64 | req.Header.Add("Authorization", "Bearer "+token) 65 | return readHTTPWithRequest(ctx, req) 66 | } 67 | -------------------------------------------------------------------------------- /tfstate/roundtrip/LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License, version 2.0 2 | 3 | 1. Definitions 4 | 5 | 1.1. “Contributor” 6 | 7 | means each individual or legal entity that creates, contributes to the 8 | creation of, or owns Covered Software. 9 | 10 | 1.2. “Contributor Version” 11 | 12 | means the combination of the Contributions of others (if any) used by a 13 | Contributor and that particular Contributor’s Contribution. 14 | 15 | 1.3. “Contribution” 16 | 17 | means Covered Software of a particular Contributor. 18 | 19 | 1.4. “Covered Software” 20 | 21 | means Source Code Form to which the initial Contributor has attached the 22 | notice in Exhibit A, the Executable Form of such Source Code Form, and 23 | Modifications of such Source Code Form, in each case including portions 24 | thereof. 25 | 26 | 1.5. “Incompatible With Secondary Licenses” 27 | means 28 | 29 | a. that the initial Contributor has attached the notice described in 30 | Exhibit B to the Covered Software; or 31 | 32 | b. that the Covered Software was made available under the terms of version 33 | 1.1 or earlier of the License, but not also under the terms of a 34 | Secondary License. 35 | 36 | 1.6. “Executable Form” 37 | 38 | means any form of the work other than Source Code Form. 39 | 40 | 1.7. “Larger Work” 41 | 42 | means a work that combines Covered Software with other material, in a separate 43 | file or files, that is not Covered Software. 44 | 45 | 1.8. “License” 46 | 47 | means this document. 48 | 49 | 1.9. “Licensable” 50 | 51 | means having the right to grant, to the maximum extent possible, whether at the 52 | time of the initial grant or subsequently, any and all of the rights conveyed by 53 | this License. 54 | 55 | 1.10. “Modifications” 56 | 57 | means any of the following: 58 | 59 | a. any file in Source Code Form that results from an addition to, deletion 60 | from, or modification of the contents of Covered Software; or 61 | 62 | b. any new file in Source Code Form that contains any Covered Software. 63 | 64 | 1.11. “Patent Claims” of a Contributor 65 | 66 | means any patent claim(s), including without limitation, method, process, 67 | and apparatus claims, in any patent Licensable by such Contributor that 68 | would be infringed, but for the grant of the License, by the making, 69 | using, selling, offering for sale, having made, import, or transfer of 70 | either its Contributions or its Contributor Version. 71 | 72 | 1.12. “Secondary License” 73 | 74 | means either the GNU General Public License, Version 2.0, the GNU Lesser 75 | General Public License, Version 2.1, the GNU Affero General Public 76 | License, Version 3.0, or any later versions of those licenses. 77 | 78 | 1.13. “Source Code Form” 79 | 80 | means the form of the work preferred for making modifications. 81 | 82 | 1.14. “You” (or “Your”) 83 | 84 | means an individual or a legal entity exercising rights under this 85 | License. For legal entities, “You” includes any entity that controls, is 86 | controlled by, or is under common control with You. For purposes of this 87 | definition, “control” means (a) the power, direct or indirect, to cause 88 | the direction or management of such entity, whether by contract or 89 | otherwise, or (b) ownership of more than fifty percent (50%) of the 90 | outstanding shares or beneficial ownership of such entity. 91 | 92 | 93 | 2. License Grants and Conditions 94 | 95 | 2.1. Grants 96 | 97 | Each Contributor hereby grants You a world-wide, royalty-free, 98 | non-exclusive license: 99 | 100 | a. under intellectual property rights (other than patent or trademark) 101 | Licensable by such Contributor to use, reproduce, make available, 102 | modify, display, perform, distribute, and otherwise exploit its 103 | Contributions, either on an unmodified basis, with Modifications, or as 104 | part of a Larger Work; and 105 | 106 | b. under Patent Claims of such Contributor to make, use, sell, offer for 107 | sale, have made, import, and otherwise transfer either its Contributions 108 | or its Contributor Version. 109 | 110 | 2.2. Effective Date 111 | 112 | The licenses granted in Section 2.1 with respect to any Contribution become 113 | effective for each Contribution on the date the Contributor first distributes 114 | such Contribution. 115 | 116 | 2.3. Limitations on Grant Scope 117 | 118 | The licenses granted in this Section 2 are the only rights granted under this 119 | License. No additional rights or licenses will be implied from the distribution 120 | or licensing of Covered Software under this License. Notwithstanding Section 121 | 2.1(b) above, no patent license is granted by a Contributor: 122 | 123 | a. for any code that a Contributor has removed from Covered Software; or 124 | 125 | b. for infringements caused by: (i) Your and any other third party’s 126 | modifications of Covered Software, or (ii) the combination of its 127 | Contributions with other software (except as part of its Contributor 128 | Version); or 129 | 130 | c. under Patent Claims infringed by Covered Software in the absence of its 131 | Contributions. 132 | 133 | This License does not grant any rights in the trademarks, service marks, or 134 | logos of any Contributor (except as may be necessary to comply with the 135 | notice requirements in Section 3.4). 136 | 137 | 2.4. Subsequent Licenses 138 | 139 | No Contributor makes additional grants as a result of Your choice to 140 | distribute the Covered Software under a subsequent version of this License 141 | (see Section 10.2) or under the terms of a Secondary License (if permitted 142 | under the terms of Section 3.3). 143 | 144 | 2.5. Representation 145 | 146 | Each Contributor represents that the Contributor believes its Contributions 147 | are its original creation(s) or it has sufficient rights to grant the 148 | rights to its Contributions conveyed by this License. 149 | 150 | 2.6. Fair Use 151 | 152 | This License is not intended to limit any rights You have under applicable 153 | copyright doctrines of fair use, fair dealing, or other equivalents. 154 | 155 | 2.7. Conditions 156 | 157 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in 158 | Section 2.1. 159 | 160 | 161 | 3. Responsibilities 162 | 163 | 3.1. Distribution of Source Form 164 | 165 | All distribution of Covered Software in Source Code Form, including any 166 | Modifications that You create or to which You contribute, must be under the 167 | terms of this License. You must inform recipients that the Source Code Form 168 | of the Covered Software is governed by the terms of this License, and how 169 | they can obtain a copy of this License. You may not attempt to alter or 170 | restrict the recipients’ rights in the Source Code 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 Form, 177 | as described in Section 3.1, and You must inform recipients of the 178 | Executable Form how they can obtain a copy of such Source Code Form by 179 | reasonable means in a timely manner, at a charge no more than the cost 180 | of distribution to the recipient; and 181 | 182 | b. You may distribute such Executable Form under the terms of this License, 183 | or sublicense it under different terms, provided that the license for 184 | the Executable Form does not attempt to limit or alter the recipients’ 185 | 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 the 191 | Covered Software. If the Larger Work is a combination of Covered Software 192 | with a work governed by one or more Secondary Licenses, and the Covered 193 | Software is not Incompatible With Secondary Licenses, this License permits 194 | You to additionally distribute such Covered Software under the terms of 195 | such Secondary License(s), so that the recipient of the Larger Work may, at 196 | their option, further distribute the Covered Software under the terms of 197 | either this License or such Secondary License(s). 198 | 199 | 3.4. Notices 200 | 201 | You may not remove or alter the substance of any license notices (including 202 | copyright notices, patent notices, disclaimers of warranty, or limitations 203 | of liability) contained within the Source Code Form of the Covered 204 | Software, except that You may alter any license notices to the extent 205 | required to remedy known factual inaccuracies. 206 | 207 | 3.5. Application of Additional Terms 208 | 209 | You may choose to offer, and to charge a fee for, warranty, support, 210 | indemnity or liability obligations to one or more recipients of Covered 211 | Software. However, You may do so only on Your own behalf, and not on behalf 212 | of any Contributor. You must make it absolutely clear that any such 213 | warranty, support, indemnity, or liability obligation is offered by You 214 | alone, and You hereby agree to indemnify every Contributor for any 215 | liability incurred by such Contributor as a result of warranty, support, 216 | indemnity or liability terms You offer. You may include additional 217 | disclaimers of warranty and limitations of liability specific to any 218 | jurisdiction. 219 | 220 | 4. Inability to Comply Due to Statute or Regulation 221 | 222 | If it is impossible for You to comply with any of the terms of this License 223 | with respect to some or all of the Covered Software due to statute, judicial 224 | order, or regulation then You must: (a) comply with the terms of this License 225 | to the maximum extent possible; and (b) describe the limitations and the code 226 | they affect. Such description must be placed in a text file included with all 227 | distributions of the Covered Software under this License. Except to the 228 | extent prohibited by statute or regulation, such description must be 229 | sufficiently detailed for a recipient of ordinary skill to be able to 230 | understand it. 231 | 232 | 5. Termination 233 | 234 | 5.1. The rights granted under this License will terminate automatically if You 235 | fail to comply with any of its terms. However, if You become compliant, 236 | then the rights granted under this License from a particular Contributor 237 | are reinstated (a) provisionally, unless and until such Contributor 238 | explicitly and finally terminates Your grants, and (b) on an ongoing basis, 239 | if such Contributor fails to notify You of the non-compliance by some 240 | reasonable means prior to 60 days after You have come back into compliance. 241 | Moreover, Your grants from a particular Contributor are reinstated on an 242 | ongoing basis if such Contributor notifies You of the non-compliance by 243 | some reasonable means, this is the first time You have received notice of 244 | non-compliance with this License from such Contributor, and You become 245 | compliant prior to 30 days after Your receipt of the notice. 246 | 247 | 5.2. If You initiate litigation against any entity by asserting a patent 248 | infringement claim (excluding declaratory judgment actions, counter-claims, 249 | and cross-claims) alleging that a Contributor Version directly or 250 | indirectly infringes any patent, then the rights granted to You by any and 251 | all Contributors for the Covered Software under Section 2.1 of this License 252 | shall terminate. 253 | 254 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user 255 | license agreements (excluding distributors and resellers) which have been 256 | validly granted by You or Your distributors under this License prior to 257 | termination shall survive termination. 258 | 259 | 6. Disclaimer of Warranty 260 | 261 | Covered Software is provided under this License on an “as is” basis, without 262 | warranty of any kind, either expressed, implied, or statutory, including, 263 | without limitation, warranties that the Covered Software is free of defects, 264 | merchantable, fit for a particular purpose or non-infringing. The entire 265 | risk as to the quality and performance of the Covered Software is with You. 266 | Should any Covered Software prove defective in any respect, You (not any 267 | Contributor) assume the cost of any necessary servicing, repair, or 268 | correction. This disclaimer of warranty constitutes an essential part of this 269 | License. No use of any Covered Software is authorized under this License 270 | except under this disclaimer. 271 | 272 | 7. Limitation of Liability 273 | 274 | Under no circumstances and under no legal theory, whether tort (including 275 | negligence), contract, or otherwise, shall any Contributor, or anyone who 276 | distributes Covered Software as permitted above, be liable to You for any 277 | direct, indirect, special, incidental, or consequential damages of any 278 | character including, without limitation, damages for lost profits, loss of 279 | goodwill, work stoppage, computer failure or malfunction, or any and all 280 | other commercial damages or losses, even if such party shall have been 281 | informed of the possibility of such damages. This limitation of liability 282 | shall not apply to liability for death or personal injury resulting from such 283 | party’s negligence to the extent applicable law prohibits such limitation. 284 | Some jurisdictions do not allow the exclusion or limitation of incidental or 285 | consequential damages, so this exclusion and limitation may not apply to You. 286 | 287 | 8. Litigation 288 | 289 | Any litigation relating to this License may be brought only in the courts of 290 | a jurisdiction where the defendant maintains its principal place of business 291 | and such litigation shall be governed by laws of that jurisdiction, without 292 | reference to its conflict-of-law provisions. Nothing in this Section shall 293 | prevent a party’s ability to bring cross-claims or counter-claims. 294 | 295 | 9. Miscellaneous 296 | 297 | This License represents the complete agreement concerning the subject matter 298 | hereof. If any provision of this License is held to be unenforceable, such 299 | provision shall be reformed only to the extent necessary to make it 300 | enforceable. Any law or regulation which provides that the language of a 301 | contract shall be construed against the drafter shall not be used to construe 302 | this License against a Contributor. 303 | 304 | 305 | 10. Versions of the License 306 | 307 | 10.1. New Versions 308 | 309 | Mozilla Foundation is the license steward. Except as provided in Section 310 | 10.3, no one other than the license steward has the right to modify or 311 | publish new versions of this License. Each version will be given a 312 | distinguishing version number. 313 | 314 | 10.2. Effect of New Versions 315 | 316 | You may distribute the Covered Software under the terms of the version of 317 | the License under which You originally received the Covered Software, or 318 | under the terms of any subsequent version published by the license 319 | steward. 320 | 321 | 10.3. Modified Versions 322 | 323 | If you create software not governed by this License, and you want to 324 | create a new license for such software, you may create and use a modified 325 | version of this License if you rename the license and remove any 326 | references to the name of the license steward (except to note that such 327 | modified license differs from this License). 328 | 329 | 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses 330 | If You choose to distribute Source Code Form that is Incompatible With 331 | Secondary Licenses under the terms of this version of the License, the 332 | notice described in Exhibit B of this License must be attached. 333 | 334 | Exhibit A - Source Code Form License Notice 335 | 336 | This Source Code Form is subject to the 337 | terms of the Mozilla Public License, v. 338 | 2.0. If a copy of the MPL was not 339 | distributed with this file, You can 340 | obtain one at 341 | http://mozilla.org/MPL/2.0/. 342 | 343 | If it is not possible or desirable to put the notice in a particular file, then 344 | You may include the notice in a location (such as a LICENSE file in a relevant 345 | directory) where a recipient would be likely to look for such a notice. 346 | 347 | You may add additional accurate notices of copyright ownership. 348 | 349 | Exhibit B - “Incompatible With Secondary Licenses” Notice 350 | 351 | This Source Code Form is “Incompatible 352 | With Secondary Licenses”, as defined by 353 | the Mozilla Public License, v. 2.0. 354 | 355 | -------------------------------------------------------------------------------- /tfstate/roundtrip/README.md: -------------------------------------------------------------------------------- 1 | This directory is copied from github.com/hashicorp/terraform/states/statefile/testdata/roundtrip. 2 | 3 | ## LICENSE 4 | 5 | See [LICENSE](LICENSE) 6 | -------------------------------------------------------------------------------- /tfstate/roundtrip/v1-simple.in.tfstate: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "serial": 1, 4 | "modules": [ 5 | { 6 | "path": [ 7 | "root" 8 | ], 9 | "outputs": { 10 | "numbers": "0,1" 11 | }, 12 | "resources": { 13 | "null_resource.bar": { 14 | "type": "null_resource", 15 | "depends_on": [ 16 | "null_resource.foo" 17 | ], 18 | "primary": { 19 | "id": "6456912646020570139", 20 | "attributes": { 21 | "id": "6456912646020570139", 22 | "triggers.#": "1", 23 | "triggers.whaaat": "0,1" 24 | } 25 | } 26 | }, 27 | "null_resource.foo.0": { 28 | "type": "null_resource", 29 | "primary": { 30 | "id": "3597404161631769617", 31 | "attributes": { 32 | "id": "3597404161631769617", 33 | "triggers.#": "1", 34 | "triggers.what": "0" 35 | } 36 | } 37 | }, 38 | "null_resource.foo.1": { 39 | "type": "null_resource", 40 | "primary": { 41 | "id": "3214385801340650197", 42 | "attributes": { 43 | "id": "3214385801340650197", 44 | "triggers.#": "1", 45 | "triggers.what": "1" 46 | } 47 | } 48 | } 49 | } 50 | } 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /tfstate/roundtrip/v1-simple.out.tfstate: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "serial": 1, 4 | "lineage": "", 5 | "terraform_version": "0.0.0", 6 | "outputs": { 7 | "numbers": { 8 | "type": "string", 9 | "value": "0,1" 10 | } 11 | }, 12 | "resources": [ 13 | { 14 | "mode": "managed", 15 | "type": "null_resource", 16 | "name": "bar", 17 | "provider": "provider[\"registry.terraform.io/-/null\"]", 18 | "instances": [ 19 | { 20 | "schema_version": 0, 21 | "attributes_flat": { 22 | "id": "6456912646020570139", 23 | "triggers.%": "1", 24 | "triggers.whaaat": "0,1" 25 | }, 26 | "depends_on": [ 27 | "null_resource.foo" 28 | ] 29 | } 30 | ] 31 | }, 32 | { 33 | "mode": "managed", 34 | "type": "null_resource", 35 | "name": "foo", 36 | "provider": "provider[\"registry.terraform.io/-/null\"]", 37 | "each": "list", 38 | "instances": [ 39 | { 40 | "index_key": 0, 41 | "schema_version": 0, 42 | "attributes_flat": { 43 | "id": "3597404161631769617", 44 | "triggers.%": "1", 45 | "triggers.what": "0" 46 | } 47 | }, 48 | { 49 | "index_key": 1, 50 | "schema_version": 0, 51 | "attributes_flat": { 52 | "id": "3214385801340650197", 53 | "triggers.%": "1", 54 | "triggers.what": "1" 55 | } 56 | } 57 | ] 58 | } 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /tfstate/roundtrip/v3-bigint.in.tfstate: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "terraform_version": "0.11.1", 4 | "serial": 8, 5 | "lineage": "0f5b2ff9-6ff5-8e9e-1f81-aa3ce9a483eb", 6 | "backend": { 7 | "hash": 10669755453527594976 8 | }, 9 | "modules": [ 10 | { 11 | "path": [ 12 | "root" 13 | ], 14 | "outputs": { 15 | "results": { 16 | "sensitive": false, 17 | "type": "map", 18 | "value": { 19 | "aws_region": "us-west-2", 20 | "list": "[{\"triggers\":{\"index\":\"0\"}},{\"triggers\":{\"index\":\"1\"}}]", 21 | "list_item_0": "{\"triggers\":{\"index\":\"0\"}}", 22 | "list_item_1": "{\"triggers\":{\"index\":\"1\"}}", 23 | "list_triggers": "[{\"index\":\"0\"},{\"index\":\"1\"}]", 24 | "list_triggers_item": "{\"index\":\"0\"}", 25 | "module_object": "{\"test\":\"hello us-west-2\",\"test2\":\"hello world 2\"}", 26 | "module_output": "hello us-west-2", 27 | "single": "{\"triggers\":{\"baz\":\"BAR\",\"cwd_path\":\"/home/mart/Devel/terraform/tmp/hcl2-simple\",\"foo\":\"bar\",\"format\":\"Hello 12\",\"json\":\"{\\\"foo\\\":\\\"bar\\\",\\\"wonk\\\":[]}\",\"module_path\":\"/home/mart/Devel/terraform/tmp/hcl2-simple\",\"root_path\":\"/home/mart/Devel/terraform/tmp/hcl2-simple\",\"woot\":\"us-west-2\",\"workspace\":\"default\"}}" 28 | } 29 | } 30 | }, 31 | "resources": { 32 | "null_resource.bar.0": { 33 | "type": "null_resource", 34 | "depends_on": [ 35 | "null_resource.baz" 36 | ], 37 | "primary": { 38 | "id": "604776346677326098", 39 | "attributes": { 40 | "id": "604776346677326098", 41 | "triggers.%": "1", 42 | "triggers.index": "0" 43 | }, 44 | "meta": { 45 | "schema_version": "1" 46 | }, 47 | "tainted": false 48 | }, 49 | "deposed": [], 50 | "provider": "provider.null" 51 | }, 52 | "null_resource.bar.1": { 53 | "type": "null_resource", 54 | "depends_on": [ 55 | "null_resource.baz" 56 | ], 57 | "primary": { 58 | "id": "4776432143683449212", 59 | "attributes": { 60 | "id": "4776432143683449212", 61 | "triggers.%": "1", 62 | "triggers.index": "1" 63 | }, 64 | "meta": {}, 65 | "tainted": false 66 | }, 67 | "deposed": [], 68 | "provider": "provider.null" 69 | }, 70 | "null_resource.baz": { 71 | "type": "null_resource", 72 | "depends_on": [], 73 | "primary": { 74 | "id": "1361331090091665738", 75 | "attributes": { 76 | "id": "1361331090091665738", 77 | "triggers.%": "9", 78 | "triggers.baz": "BAR", 79 | "triggers.cwd_path": "/home/mart/Devel/terraform/tmp/hcl2-simple", 80 | "triggers.foo": "bar", 81 | "triggers.format": "Hello 12", 82 | "triggers.json": "{\"foo\":\"bar\",\"wonk\":[]}", 83 | "triggers.module_path": "/home/mart/Devel/terraform/tmp/hcl2-simple", 84 | "triggers.root_path": "/home/mart/Devel/terraform/tmp/hcl2-simple", 85 | "triggers.woot": "us-west-2", 86 | "triggers.workspace": "default" 87 | }, 88 | "meta": { 89 | "foo": "bar" 90 | }, 91 | "tainted": false 92 | }, 93 | "deposed": [], 94 | "provider": "provider.null" 95 | } 96 | }, 97 | "depends_on": [] 98 | }, 99 | { 100 | "path": [ 101 | "root", 102 | "child" 103 | ], 104 | "outputs": { 105 | "test": { 106 | "sensitive": false, 107 | "type": "string", 108 | "value": "hello us-west-2" 109 | }, 110 | "test2": { 111 | "sensitive": false, 112 | "type": "string", 113 | "value": "hello world 2" 114 | } 115 | }, 116 | "resources": { 117 | "null_resource.foo": { 118 | "type": "null_resource", 119 | "depends_on": [], 120 | "primary": { 121 | "id": "1361", 122 | "attributes": { 123 | "id": "1361", 124 | "triggers.%": "0" 125 | }, 126 | "meta": {}, 127 | "tainted": false 128 | }, 129 | "deposed": [], 130 | "provider": "provider.null" 131 | } 132 | }, 133 | "depends_on": [] 134 | } 135 | ] 136 | } 137 | -------------------------------------------------------------------------------- /tfstate/roundtrip/v3-bigint.out.tfstate: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "terraform_version": "0.11.1", 4 | "serial": 8, 5 | "lineage": "0f5b2ff9-6ff5-8e9e-1f81-aa3ce9a483eb", 6 | "outputs": { 7 | "results": { 8 | "type": [ 9 | "map", 10 | "string" 11 | ], 12 | "value": { 13 | "aws_region": "us-west-2", 14 | "list": "[{\"triggers\":{\"index\":\"0\"}},{\"triggers\":{\"index\":\"1\"}}]", 15 | "list_item_0": "{\"triggers\":{\"index\":\"0\"}}", 16 | "list_item_1": "{\"triggers\":{\"index\":\"1\"}}", 17 | "list_triggers": "[{\"index\":\"0\"},{\"index\":\"1\"}]", 18 | "list_triggers_item": "{\"index\":\"0\"}", 19 | "module_object": "{\"test\":\"hello us-west-2\",\"test2\":\"hello world 2\"}", 20 | "module_output": "hello us-west-2", 21 | "single": "{\"triggers\":{\"baz\":\"BAR\",\"cwd_path\":\"/home/mart/Devel/terraform/tmp/hcl2-simple\",\"foo\":\"bar\",\"format\":\"Hello 12\",\"json\":\"{\\\"foo\\\":\\\"bar\\\",\\\"wonk\\\":[]}\",\"module_path\":\"/home/mart/Devel/terraform/tmp/hcl2-simple\",\"root_path\":\"/home/mart/Devel/terraform/tmp/hcl2-simple\",\"woot\":\"us-west-2\",\"workspace\":\"default\"}}" 22 | } 23 | } 24 | }, 25 | "resources": [ 26 | { 27 | "mode": "managed", 28 | "type": "null_resource", 29 | "name": "bar", 30 | "each": "list", 31 | "provider": "provider.null", 32 | "instances": [ 33 | { 34 | "attributes_flat": { 35 | "id": "604776346677326098", 36 | "triggers.%": "1", 37 | "triggers.index": "0" 38 | }, 39 | "depends_on": [ 40 | "null_resource.baz" 41 | ], 42 | "index_key": 0, 43 | "schema_version": 1 44 | }, 45 | { 46 | "attributes_flat": { 47 | "id": "4776432143683449212", 48 | "triggers.%": "1", 49 | "triggers.index": "1" 50 | }, 51 | "depends_on": [ 52 | "null_resource.baz" 53 | ], 54 | "index_key": 1, 55 | "schema_version": 0 56 | } 57 | ] 58 | }, 59 | { 60 | "mode": "managed", 61 | "type": "null_resource", 62 | "name": "baz", 63 | "provider": "provider.null", 64 | "instances": [ 65 | { 66 | "attributes_flat": { 67 | "id": "1361331090091665738", 68 | "triggers.%": "9", 69 | "triggers.baz": "BAR", 70 | "triggers.cwd_path": "/home/mart/Devel/terraform/tmp/hcl2-simple", 71 | "triggers.foo": "bar", 72 | "triggers.format": "Hello 12", 73 | "triggers.json": "{\"foo\":\"bar\",\"wonk\":[]}", 74 | "triggers.module_path": "/home/mart/Devel/terraform/tmp/hcl2-simple", 75 | "triggers.root_path": "/home/mart/Devel/terraform/tmp/hcl2-simple", 76 | "triggers.woot": "us-west-2", 77 | "triggers.workspace": "default" 78 | }, 79 | "schema_version": 0, 80 | "private": "eyJmb28iOiJiYXIifQ==" 81 | } 82 | ] 83 | }, 84 | { 85 | "module": "module.child", 86 | "mode": "managed", 87 | "type": "null_resource", 88 | "name": "foo", 89 | "provider": "provider.null", 90 | "instances": [ 91 | { 92 | "attributes_flat": { 93 | "id": "1361", 94 | "triggers.%": "0" 95 | }, 96 | "schema_version": 0 97 | } 98 | ] 99 | } 100 | ] 101 | } 102 | -------------------------------------------------------------------------------- /tfstate/roundtrip/v3-grabbag.in.tfstate: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "terraform_version": "0.11.1", 4 | "serial": 8, 5 | "lineage": "0f5b2ff9-6ff5-8e9e-1f81-aa3ce9a483eb", 6 | "modules": [ 7 | { 8 | "path": [ 9 | "root" 10 | ], 11 | "outputs": { 12 | "results": { 13 | "sensitive": false, 14 | "type": "map", 15 | "value": { 16 | "aws_region": "us-west-2", 17 | "list": "[{\"triggers\":{\"index\":\"0\"}},{\"triggers\":{\"index\":\"1\"}}]", 18 | "list_item_0": "{\"triggers\":{\"index\":\"0\"}}", 19 | "list_item_1": "{\"triggers\":{\"index\":\"1\"}}", 20 | "list_triggers": "[{\"index\":\"0\"},{\"index\":\"1\"}]", 21 | "list_triggers_item": "{\"index\":\"0\"}", 22 | "module_object": "{\"test\":\"hello us-west-2\",\"test2\":\"hello world 2\"}", 23 | "module_output": "hello us-west-2", 24 | "single": "{\"triggers\":{\"baz\":\"BAR\",\"cwd_path\":\"/home/mart/Devel/terraform/tmp/hcl2-simple\",\"foo\":\"bar\",\"format\":\"Hello 12\",\"json\":\"{\\\"foo\\\":\\\"bar\\\",\\\"wonk\\\":[]}\",\"module_path\":\"/home/mart/Devel/terraform/tmp/hcl2-simple\",\"root_path\":\"/home/mart/Devel/terraform/tmp/hcl2-simple\",\"woot\":\"us-west-2\",\"workspace\":\"default\"}}" 25 | } 26 | } 27 | }, 28 | "resources": { 29 | "null_resource.bar.0": { 30 | "type": "null_resource", 31 | "depends_on": [ 32 | "null_resource.baz" 33 | ], 34 | "primary": { 35 | "id": "604776346677326098", 36 | "attributes": { 37 | "id": "604776346677326098", 38 | "triggers.%": "1", 39 | "triggers.index": "0" 40 | }, 41 | "meta": { 42 | "schema_version": "1" 43 | }, 44 | "tainted": false 45 | }, 46 | "deposed": [], 47 | "provider": "provider.null" 48 | }, 49 | "null_resource.bar.1": { 50 | "type": "null_resource", 51 | "depends_on": [ 52 | "null_resource.baz" 53 | ], 54 | "primary": { 55 | "id": "4776432143683449212", 56 | "attributes": { 57 | "id": "4776432143683449212", 58 | "triggers.%": "1", 59 | "triggers.index": "1" 60 | }, 61 | "meta": {}, 62 | "tainted": false 63 | }, 64 | "deposed": [], 65 | "provider": "provider.null" 66 | }, 67 | "null_resource.baz": { 68 | "type": "null_resource", 69 | "depends_on": [], 70 | "primary": { 71 | "id": "1361331090091665738", 72 | "attributes": { 73 | "id": "1361331090091665738", 74 | "triggers.%": "9", 75 | "triggers.baz": "BAR", 76 | "triggers.cwd_path": "/home/mart/Devel/terraform/tmp/hcl2-simple", 77 | "triggers.foo": "bar", 78 | "triggers.format": "Hello 12", 79 | "triggers.json": "{\"foo\":\"bar\",\"wonk\":[]}", 80 | "triggers.module_path": "/home/mart/Devel/terraform/tmp/hcl2-simple", 81 | "triggers.root_path": "/home/mart/Devel/terraform/tmp/hcl2-simple", 82 | "triggers.woot": "us-west-2", 83 | "triggers.workspace": "default" 84 | }, 85 | "meta": { 86 | "foo": "bar" 87 | }, 88 | "tainted": false 89 | }, 90 | "deposed": [], 91 | "provider": "provider.null" 92 | } 93 | }, 94 | "depends_on": [] 95 | }, 96 | { 97 | "path": [ 98 | "root", 99 | "child" 100 | ], 101 | "outputs": { 102 | "test": { 103 | "sensitive": false, 104 | "type": "string", 105 | "value": "hello us-west-2" 106 | }, 107 | "test2": { 108 | "sensitive": false, 109 | "type": "string", 110 | "value": "hello world 2" 111 | } 112 | }, 113 | "resources": { 114 | "null_resource.foo": { 115 | "type": "null_resource", 116 | "depends_on": [], 117 | "primary": { 118 | "id": "1361", 119 | "attributes": { 120 | "id": "1361", 121 | "triggers.%": "0" 122 | }, 123 | "meta": {}, 124 | "tainted": false 125 | }, 126 | "deposed": [], 127 | "provider": "provider.null" 128 | } 129 | }, 130 | "depends_on": [] 131 | } 132 | ] 133 | } 134 | -------------------------------------------------------------------------------- /tfstate/roundtrip/v3-grabbag.out.tfstate: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "terraform_version": "0.11.1", 4 | "serial": 8, 5 | "lineage": "0f5b2ff9-6ff5-8e9e-1f81-aa3ce9a483eb", 6 | "outputs": { 7 | "results": { 8 | "type": [ 9 | "map", 10 | "string" 11 | ], 12 | "value": { 13 | "aws_region": "us-west-2", 14 | "list": "[{\"triggers\":{\"index\":\"0\"}},{\"triggers\":{\"index\":\"1\"}}]", 15 | "list_item_0": "{\"triggers\":{\"index\":\"0\"}}", 16 | "list_item_1": "{\"triggers\":{\"index\":\"1\"}}", 17 | "list_triggers": "[{\"index\":\"0\"},{\"index\":\"1\"}]", 18 | "list_triggers_item": "{\"index\":\"0\"}", 19 | "module_object": "{\"test\":\"hello us-west-2\",\"test2\":\"hello world 2\"}", 20 | "module_output": "hello us-west-2", 21 | "single": "{\"triggers\":{\"baz\":\"BAR\",\"cwd_path\":\"/home/mart/Devel/terraform/tmp/hcl2-simple\",\"foo\":\"bar\",\"format\":\"Hello 12\",\"json\":\"{\\\"foo\\\":\\\"bar\\\",\\\"wonk\\\":[]}\",\"module_path\":\"/home/mart/Devel/terraform/tmp/hcl2-simple\",\"root_path\":\"/home/mart/Devel/terraform/tmp/hcl2-simple\",\"woot\":\"us-west-2\",\"workspace\":\"default\"}}" 22 | } 23 | } 24 | }, 25 | "resources": [ 26 | { 27 | "mode": "managed", 28 | "type": "null_resource", 29 | "name": "bar", 30 | "each": "list", 31 | "provider": "provider.null", 32 | "instances": [ 33 | { 34 | "attributes_flat": { 35 | "id": "604776346677326098", 36 | "triggers.%": "1", 37 | "triggers.index": "0" 38 | }, 39 | "depends_on": [ 40 | "null_resource.baz" 41 | ], 42 | "index_key": 0, 43 | "schema_version": 1 44 | }, 45 | { 46 | "attributes_flat": { 47 | "id": "4776432143683449212", 48 | "triggers.%": "1", 49 | "triggers.index": "1" 50 | }, 51 | "depends_on": [ 52 | "null_resource.baz" 53 | ], 54 | "index_key": 1, 55 | "schema_version": 0 56 | } 57 | ] 58 | }, 59 | { 60 | "mode": "managed", 61 | "type": "null_resource", 62 | "name": "baz", 63 | "provider": "provider.null", 64 | "instances": [ 65 | { 66 | "attributes_flat": { 67 | "id": "1361331090091665738", 68 | "triggers.%": "9", 69 | "triggers.baz": "BAR", 70 | "triggers.cwd_path": "/home/mart/Devel/terraform/tmp/hcl2-simple", 71 | "triggers.foo": "bar", 72 | "triggers.format": "Hello 12", 73 | "triggers.json": "{\"foo\":\"bar\",\"wonk\":[]}", 74 | "triggers.module_path": "/home/mart/Devel/terraform/tmp/hcl2-simple", 75 | "triggers.root_path": "/home/mart/Devel/terraform/tmp/hcl2-simple", 76 | "triggers.woot": "us-west-2", 77 | "triggers.workspace": "default" 78 | }, 79 | "schema_version": 0, 80 | "private": "eyJmb28iOiJiYXIifQ==" 81 | } 82 | ] 83 | }, 84 | { 85 | "module": "module.child", 86 | "mode": "managed", 87 | "type": "null_resource", 88 | "name": "foo", 89 | "provider": "provider.null", 90 | "instances": [ 91 | { 92 | "attributes_flat": { 93 | "id": "1361", 94 | "triggers.%": "0" 95 | }, 96 | "schema_version": 0 97 | } 98 | ] 99 | } 100 | ] 101 | } 102 | -------------------------------------------------------------------------------- /tfstate/roundtrip/v3-invalid-depends.in.tfstate: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "terraform_version": "0.7.13", 4 | "serial": 0, 5 | "lineage": "f2968801-fa14-41ab-a044-224f3a4adf04", 6 | "modules": [ 7 | { 8 | "path": [ 9 | "root" 10 | ], 11 | "outputs": { 12 | "numbers": { 13 | "sensitive": false, 14 | "type": "string", 15 | "value": "0,1" 16 | } 17 | }, 18 | "resources": { 19 | "null_resource.bar": { 20 | "type": "null_resource", 21 | "depends_on": [ 22 | "null_resource.valid", 23 | "null_resource.1invalid" 24 | ], 25 | "primary": { 26 | "id": "5388490630832483079", 27 | "attributes": { 28 | "id": "5388490630832483079", 29 | "triggers.%": "1", 30 | "triggers.whaaat": "0,1" 31 | }, 32 | "meta": {}, 33 | "tainted": false 34 | }, 35 | "deposed": [], 36 | "provider": "" 37 | } 38 | }, 39 | "depends_on": [] 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /tfstate/roundtrip/v3-invalid-depends.out.tfstate: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "serial": 0, 4 | "lineage": "f2968801-fa14-41ab-a044-224f3a4adf04", 5 | "terraform_version": "0.7.13", 6 | "outputs": { 7 | "numbers": { 8 | "type": "string", 9 | "value": "0,1" 10 | } 11 | }, 12 | "resources": [ 13 | { 14 | "mode": "managed", 15 | "type": "null_resource", 16 | "name": "bar", 17 | "provider": "provider.null", 18 | "instances": [ 19 | { 20 | "schema_version": 0, 21 | "attributes_flat": { 22 | "id": "5388490630832483079", 23 | "triggers.%": "1", 24 | "triggers.whaaat": "0,1" 25 | }, 26 | "depends_on": [ 27 | "null_resource.valid" 28 | ] 29 | } 30 | ] 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /tfstate/roundtrip/v3-simple.in.tfstate: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "terraform_version": "0.7.13", 4 | "serial": 0, 5 | "lineage": "f2968801-fa14-41ab-a044-224f3a4adf04", 6 | "modules": [ 7 | { 8 | "path": [ 9 | "root" 10 | ], 11 | "outputs": { 12 | "numbers": { 13 | "sensitive": false, 14 | "type": "string", 15 | "value": "0,1" 16 | } 17 | }, 18 | "resources": { 19 | "null_resource.bar": { 20 | "type": "null_resource", 21 | "depends_on": [ 22 | "null_resource.foo.*", 23 | "null_resource.foobar", 24 | "null_resource.foobar.1" 25 | ], 26 | "primary": { 27 | "id": "5388490630832483079", 28 | "attributes": { 29 | "id": "5388490630832483079", 30 | "triggers.%": "1", 31 | "triggers.whaaat": "0,1" 32 | }, 33 | "meta": {}, 34 | "tainted": false 35 | }, 36 | "deposed": [], 37 | "provider": "" 38 | }, 39 | "null_resource.foo.0": { 40 | "type": "null_resource", 41 | "depends_on": [], 42 | "primary": { 43 | "id": "8212585058302700791", 44 | "attributes": { 45 | "id": "8212585058302700791", 46 | "triggers.%": "1", 47 | "triggers.what": "0" 48 | }, 49 | "meta": {}, 50 | "tainted": false 51 | }, 52 | "deposed": [], 53 | "provider": "" 54 | }, 55 | "null_resource.foo.1": { 56 | "type": "null_resource", 57 | "depends_on": [], 58 | "primary": { 59 | "id": "1523897709610803586", 60 | "attributes": { 61 | "id": "1523897709610803586", 62 | "triggers.%": "1", 63 | "triggers.what": "1" 64 | }, 65 | "meta": {}, 66 | "tainted": false 67 | }, 68 | "deposed": [], 69 | "provider": "" 70 | }, 71 | "null_resource.foobar": { 72 | "type": "null_resource", 73 | "depends_on": [], 74 | "primary": { 75 | "id": "7388490630832483079", 76 | "attributes": { 77 | "id": "7388490630832483079", 78 | "triggers.%": "1", 79 | "triggers.whaaat": "0,1" 80 | }, 81 | "meta": {}, 82 | "tainted": false 83 | }, 84 | "deposed": [], 85 | "provider": "" 86 | } 87 | }, 88 | "depends_on": [] 89 | } 90 | ] 91 | } -------------------------------------------------------------------------------- /tfstate/roundtrip/v3-simple.out.tfstate: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "serial": 0, 4 | "lineage": "f2968801-fa14-41ab-a044-224f3a4adf04", 5 | "terraform_version": "0.7.13", 6 | "outputs": { 7 | "numbers": { 8 | "type": "string", 9 | "value": "0,1" 10 | } 11 | }, 12 | "resources": [ 13 | { 14 | "mode": "managed", 15 | "type": "null_resource", 16 | "name": "bar", 17 | "provider": "provider.null", 18 | "instances": [ 19 | { 20 | "schema_version": 0, 21 | "attributes_flat": { 22 | "id": "5388490630832483079", 23 | "triggers.%": "1", 24 | "triggers.whaaat": "0,1" 25 | }, 26 | "depends_on": [ 27 | "null_resource.foo", 28 | "null_resource.foobar", 29 | "null_resource.foobar[1]" 30 | ] 31 | } 32 | ] 33 | }, 34 | { 35 | "mode": "managed", 36 | "type": "null_resource", 37 | "name": "foo", 38 | "provider": "provider.null", 39 | "each": "list", 40 | "instances": [ 41 | { 42 | "index_key": 0, 43 | "schema_version": 0, 44 | "attributes_flat": { 45 | "id": "8212585058302700791", 46 | "triggers.%": "1", 47 | "triggers.what": "0" 48 | } 49 | }, 50 | { 51 | "index_key": 1, 52 | "schema_version": 0, 53 | "attributes_flat": { 54 | "id": "1523897709610803586", 55 | "triggers.%": "1", 56 | "triggers.what": "1" 57 | } 58 | } 59 | ] 60 | }, 61 | { 62 | "mode": "managed", 63 | "type": "null_resource", 64 | "name": "foobar", 65 | "provider": "provider.null", 66 | "instances": [ 67 | { 68 | "schema_version": 0, 69 | "attributes_flat": { 70 | "id": "7388490630832483079", 71 | "triggers.%": "1", 72 | "triggers.whaaat": "0,1" 73 | } 74 | } 75 | ] 76 | } 77 | ] 78 | } 79 | -------------------------------------------------------------------------------- /tfstate/roundtrip/v4-cbd.in.tfstate: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "serial": 0, 4 | "lineage": "f2968801-fa14-41ab-a044-224f3a4adf04", 5 | "terraform_version": "0.12.0", 6 | "outputs": { 7 | "numbers": { 8 | "type": "string", 9 | "value": "0,1" 10 | } 11 | }, 12 | "resources": [ 13 | { 14 | "module": "module.modA", 15 | "mode": "managed", 16 | "type": "null_resource", 17 | "name": "resource", 18 | "provider": "provider[\"registry.terraform.io/-/null\"]", 19 | "instances": [ 20 | { 21 | "schema_version": 0, 22 | "attributes": { 23 | "id": "4639265839606265182", 24 | "triggers": { 25 | "input": "test" 26 | } 27 | }, 28 | "create_before_destroy": true, 29 | "private": "bnVsbA==" 30 | } 31 | ] 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /tfstate/roundtrip/v4-cbd.out.tfstate: -------------------------------------------------------------------------------- 1 | v4-cbd.in.tfstate -------------------------------------------------------------------------------- /tfstate/roundtrip/v4-foreach.in.tfstate: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "serial": 0, 4 | "lineage": "f2968801-fa14-41ab-a044-224f3a4adf04", 5 | "terraform_version": "0.12.0", 6 | "outputs": { 7 | "numbers": { 8 | "type": "string", 9 | "value": "0,1" 10 | } 11 | }, 12 | "resources": [ 13 | { 14 | "module": "module.modA", 15 | "mode": "managed", 16 | "type": "null_resource", 17 | "name": "resource", 18 | "provider": "provider[\"registry.terraform.io/-/null\"]", 19 | "instances": [ 20 | { 21 | "schema_version": 0, 22 | "attributes": { 23 | "id": "4639265839606265182", 24 | "triggers": { 25 | "input": "test" 26 | } 27 | }, 28 | "private": "bnVsbA==", 29 | "depends_on": [ 30 | "var.input" 31 | ] 32 | } 33 | ] 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /tfstate/roundtrip/v4-foreach.out.tfstate: -------------------------------------------------------------------------------- 1 | v4-foreach.in.tfstate -------------------------------------------------------------------------------- /tfstate/roundtrip/v4-legacy-foreach.in.tfstate: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "serial": 0, 4 | "lineage": "f2968801-fa14-41ab-a044-224f3a4adf04", 5 | "terraform_version": "0.12.0", 6 | "outputs": { 7 | "numbers": { 8 | "type": "string", 9 | "value": "0,1" 10 | } 11 | }, 12 | "resources": [ 13 | { 14 | "module": "module.modA", 15 | "mode": "managed", 16 | "type": "null_resource", 17 | "name": "resource", 18 | "provider": "provider.null", 19 | "instances": [ 20 | { 21 | "schema_version": 0, 22 | "attributes": { 23 | "id": "4639265839606265182", 24 | "triggers": { 25 | "input": "test" 26 | } 27 | }, 28 | "private": "bnVsbA==", 29 | "depends_on": [ 30 | "var.input" 31 | ] 32 | } 33 | ] 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /tfstate/roundtrip/v4-legacy-foreach.out.tfstate: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "serial": 0, 4 | "lineage": "f2968801-fa14-41ab-a044-224f3a4adf04", 5 | "terraform_version": "0.12.0", 6 | "outputs": { 7 | "numbers": { 8 | "type": "string", 9 | "value": "0,1" 10 | } 11 | }, 12 | "resources": [ 13 | { 14 | "module": "module.modA", 15 | "mode": "managed", 16 | "type": "null_resource", 17 | "name": "resource", 18 | "provider": "provider[\"registry.terraform.io/-/null\"]", 19 | "instances": [ 20 | { 21 | "schema_version": 0, 22 | "attributes": { 23 | "id": "4639265839606265182", 24 | "triggers": { 25 | "input": "test" 26 | } 27 | }, 28 | "private": "bnVsbA==", 29 | "depends_on": [ 30 | "var.input" 31 | ] 32 | } 33 | ] 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /tfstate/roundtrip/v4-legacy-modules.in.tfstate: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "terraform_version": "0.12.0", 4 | "serial": 0, 5 | "lineage": "f2968801-fa14-41ab-a044-224f3a4adf04", 6 | "outputs": { 7 | "numbers": { 8 | "value": "0,1", 9 | "type": "string" 10 | } 11 | }, 12 | "resources": [ 13 | { 14 | "mode": "managed", 15 | "type": "null_resource", 16 | "name": "bar", 17 | "provider": "provider.null", 18 | "instances": [ 19 | { 20 | "schema_version": 0, 21 | "attributes_flat": { 22 | "id": "5388490630832483079", 23 | "triggers.%": "1", 24 | "triggers.whaaat": "0,1" 25 | }, 26 | "depends_on": [ 27 | "null_resource.foo" 28 | ] 29 | } 30 | ] 31 | }, 32 | { 33 | "module": "module.modB", 34 | "mode": "managed", 35 | "type": "null_resource", 36 | "name": "bar", 37 | "each": "map", 38 | "provider": "provider. null", 39 | "instances": [ 40 | { 41 | "index_key": "a", 42 | "schema_version": 0, 43 | "attributes_flat": { 44 | "id": "8212585058302700791" 45 | }, 46 | "dependencies": [ 47 | "module.modA.null_resource.resource" 48 | ] 49 | }, 50 | { 51 | "index_key": "b", 52 | "schema_version": 0, 53 | "attributes_flat": { 54 | "id": "1523897709610803586" 55 | }, 56 | "dependencies": [ 57 | "module.modA.null_resource.resource" 58 | ] 59 | } 60 | ] 61 | }, 62 | { 63 | "module": "module.modA", 64 | "mode": "managed", 65 | "type": "null_resource", 66 | "name": "resource", 67 | "provider": "provider.null", 68 | "instances": [ 69 | { 70 | "schema_version": 0, 71 | "attributes": { 72 | "id": "4639265839606265182", 73 | "triggers": { 74 | "input": "test" 75 | } 76 | }, 77 | "private": "bnVsbA==", 78 | "dependencies": [ 79 | "null_resource.bar" 80 | ], 81 | "depends_on": [ 82 | "var.input" 83 | ] 84 | } 85 | ] 86 | } 87 | ] 88 | } 89 | -------------------------------------------------------------------------------- /tfstate/roundtrip/v4-legacy-modules.out.tfstate: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "terraform_version": "0.12.0", 4 | "serial": 0, 5 | "lineage": "f2968801-fa14-41ab-a044-224f3a4adf04", 6 | "outputs": { 7 | "numbers": { 8 | "value": "0,1", 9 | "type": "string" 10 | } 11 | }, 12 | "resources": [ 13 | { 14 | "mode": "managed", 15 | "type": "null_resource", 16 | "name": "bar", 17 | "provider": "provider[\"registry.terraform.io/-/null\"]", 18 | "instances": [ 19 | { 20 | "schema_version": 0, 21 | "attributes_flat": { 22 | "id": "5388490630832483079", 23 | "triggers.%": "1", 24 | "triggers.whaaat": "0,1" 25 | }, 26 | "depends_on": [ 27 | "null_resource.foo" 28 | ] 29 | } 30 | ] 31 | }, 32 | { 33 | "module": "module.modB", 34 | "mode": "managed", 35 | "type": "null_resource", 36 | "name": "bar", 37 | "each": "map", 38 | "provider": "provider[\"registry.terraform.io/-/null\"]", 39 | "instances": [ 40 | { 41 | "index_key": "a", 42 | "schema_version": 0, 43 | "attributes_flat": { 44 | "id": "8212585058302700791" 45 | }, 46 | "dependencies": [ 47 | "module.modA.null_resource.resource" 48 | ] 49 | }, 50 | { 51 | "index_key": "b", 52 | "schema_version": 0, 53 | "attributes_flat": { 54 | "id": "1523897709610803586" 55 | }, 56 | "dependencies": [ 57 | "module.modA.null_resource.resource" 58 | ] 59 | } 60 | ] 61 | }, 62 | { 63 | "module": "module.modA", 64 | "mode": "managed", 65 | "type": "null_resource", 66 | "name": "resource", 67 | "provider": "provider[\"registry.terraform.io/-/null\"]", 68 | "instances": [ 69 | { 70 | "schema_version": 0, 71 | "attributes": { 72 | "id": "4639265839606265182", 73 | "triggers": { 74 | "input": "test" 75 | } 76 | }, 77 | "private": "bnVsbA==", 78 | "dependencies": [ 79 | "null_resource.bar" 80 | ], 81 | "depends_on": [ 82 | "var.input" 83 | ] 84 | } 85 | ] 86 | } 87 | ] 88 | } 89 | -------------------------------------------------------------------------------- /tfstate/roundtrip/v4-legacy-simple.in.tfstate: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "serial": 0, 4 | "lineage": "f2968801-fa14-41ab-a044-224f3a4adf04", 5 | "terraform_version": "0.12.0", 6 | "outputs": { 7 | "numbers": { 8 | "type": "string", 9 | "value": "0,1" 10 | } 11 | }, 12 | "resources": [ 13 | { 14 | "mode": "managed", 15 | "type": "null_resource", 16 | "name": "bar", 17 | "provider": "provider.null", 18 | "instances": [ 19 | { 20 | "schema_version": 0, 21 | "attributes_flat": { 22 | "id": "5388490630832483079", 23 | "triggers.%": "1", 24 | "triggers.whaaat": "0,1" 25 | }, 26 | "depends_on": [ 27 | "null_resource.foo" 28 | ] 29 | } 30 | ] 31 | }, 32 | { 33 | "mode": "managed", 34 | "type": "null_resource", 35 | "name": "foo", 36 | "provider": "provider.null", 37 | "each": "list", 38 | "instances": [ 39 | { 40 | "index_key": 0, 41 | "schema_version": 0, 42 | "attributes_flat": { 43 | "id": "8212585058302700791", 44 | "triggers.%": "1", 45 | "triggers.what": "0" 46 | } 47 | }, 48 | { 49 | "index_key": 1, 50 | "schema_version": 0, 51 | "attributes_flat": { 52 | "id": "1523897709610803586", 53 | "triggers.%": "1", 54 | "triggers.what": "0" 55 | } 56 | } 57 | ] 58 | } 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /tfstate/roundtrip/v4-legacy-simple.out.tfstate: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "serial": 0, 4 | "lineage": "f2968801-fa14-41ab-a044-224f3a4adf04", 5 | "terraform_version": "0.12.0", 6 | "outputs": { 7 | "numbers": { 8 | "type": "string", 9 | "value": "0,1" 10 | } 11 | }, 12 | "resources": [ 13 | { 14 | "mode": "managed", 15 | "type": "null_resource", 16 | "name": "bar", 17 | "provider": "provider[\"registry.terraform.io/-/null\"]", 18 | "instances": [ 19 | { 20 | "schema_version": 0, 21 | "attributes_flat": { 22 | "id": "5388490630832483079", 23 | "triggers.%": "1", 24 | "triggers.whaaat": "0,1" 25 | }, 26 | "depends_on": [ 27 | "null_resource.foo" 28 | ] 29 | } 30 | ] 31 | }, 32 | { 33 | "mode": "managed", 34 | "type": "null_resource", 35 | "name": "foo", 36 | "provider": "provider.null", 37 | "each": "list", 38 | "instances": [ 39 | { 40 | "index_key": 0, 41 | "schema_version": 0, 42 | "attributes_flat": { 43 | "id": "8212585058302700791", 44 | "triggers.%": "1", 45 | "triggers.what": "0" 46 | } 47 | }, 48 | { 49 | "index_key": 1, 50 | "schema_version": 0, 51 | "attributes_flat": { 52 | "id": "1523897709610803586", 53 | "triggers.%": "1", 54 | "triggers.what": "0" 55 | } 56 | } 57 | ] 58 | } 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /tfstate/roundtrip/v4-modules.in.tfstate: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "terraform_version": "0.12.0", 4 | "serial": 0, 5 | "lineage": "f2968801-fa14-41ab-a044-224f3a4adf04", 6 | "outputs": { 7 | "numbers": { 8 | "value": "0,1", 9 | "type": "string" 10 | } 11 | }, 12 | "resources": [ 13 | { 14 | "mode": "managed", 15 | "type": "null_resource", 16 | "name": "bar", 17 | "provider": "provider[\"registry.terraform.io/-/null\"]", 18 | "instances": [ 19 | { 20 | "schema_version": 0, 21 | "attributes_flat": { 22 | "id": "5388490630832483079", 23 | "triggers.%": "1", 24 | "triggers.whaaat": "0,1" 25 | }, 26 | "depends_on": [ 27 | "null_resource.foo" 28 | ] 29 | } 30 | ] 31 | }, 32 | { 33 | "module": "module.modB", 34 | "mode": "managed", 35 | "type": "null_resource", 36 | "name": "bar", 37 | "each": "map", 38 | "provider": "provider[\"registry.terraform.io/-/null\"]", 39 | "instances": [ 40 | { 41 | "index_key": "a", 42 | "schema_version": 0, 43 | "attributes_flat": { 44 | "id": "8212585058302700791" 45 | }, 46 | "dependencies": [ 47 | "module.modA.null_resource.resource" 48 | ] 49 | }, 50 | { 51 | "index_key": "b", 52 | "schema_version": 0, 53 | "attributes_flat": { 54 | "id": "1523897709610803586" 55 | }, 56 | "dependencies": [ 57 | "module.modA.null_resource.resource" 58 | ] 59 | } 60 | ] 61 | }, 62 | { 63 | "module": "module.modA", 64 | "mode": "managed", 65 | "type": "null_resource", 66 | "name": "resource", 67 | "provider": "provider[\"registry.terraform.io/-/null\"]", 68 | "instances": [ 69 | { 70 | "schema_version": 0, 71 | "attributes": { 72 | "id": "4639265839606265182", 73 | "triggers": { 74 | "input": "test" 75 | } 76 | }, 77 | "private": "bnVsbA==", 78 | "dependencies": [ 79 | "null_resource.bar" 80 | ], 81 | "depends_on": [ 82 | "var.input" 83 | ] 84 | } 85 | ] 86 | } 87 | ] 88 | } 89 | -------------------------------------------------------------------------------- /tfstate/roundtrip/v4-modules.out.tfstate: -------------------------------------------------------------------------------- 1 | v4-modules.in.tfstate -------------------------------------------------------------------------------- /tfstate/roundtrip/v4-simple.in.tfstate: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "serial": 0, 4 | "lineage": "f2968801-fa14-41ab-a044-224f3a4adf04", 5 | "terraform_version": "0.12.0", 6 | "outputs": { 7 | "numbers": { 8 | "type": "string", 9 | "value": "0,1" 10 | } 11 | }, 12 | "resources": [ 13 | { 14 | "mode": "managed", 15 | "type": "null_resource", 16 | "name": "bar", 17 | "provider": "provider[\"registry.terraform.io/-/null\"]", 18 | "instances": [ 19 | { 20 | "schema_version": 0, 21 | "attributes_flat": { 22 | "id": "5388490630832483079", 23 | "triggers.%": "1", 24 | "triggers.whaaat": "0,1" 25 | }, 26 | "depends_on": [ 27 | "null_resource.foo" 28 | ] 29 | } 30 | ] 31 | }, 32 | { 33 | "mode": "managed", 34 | "type": "null_resource", 35 | "name": "foo", 36 | "provider": "provider[\"registry.terraform.io/-/null\"]", 37 | "each": "list", 38 | "instances": [ 39 | { 40 | "index_key": 0, 41 | "schema_version": 0, 42 | "attributes_flat": { 43 | "id": "8212585058302700791", 44 | "triggers.%": "1", 45 | "triggers.what": "0" 46 | } 47 | }, 48 | { 49 | "index_key": 1, 50 | "schema_version": 0, 51 | "attributes_flat": { 52 | "id": "1523897709610803586", 53 | "triggers.%": "1", 54 | "triggers.what": "0" 55 | } 56 | } 57 | ] 58 | } 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /tfstate/roundtrip/v4-simple.out.tfstate: -------------------------------------------------------------------------------- 1 | v4-simple.in.tfstate -------------------------------------------------------------------------------- /tfstate/roundtrip_test.go: -------------------------------------------------------------------------------- 1 | package tfstate_test 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | "strings" 9 | "testing" 10 | 11 | "github.com/fujiwara/tfstate-lookup/tfstate" 12 | ) 13 | 14 | func TestRoundTrip(t *testing.T) { 15 | err := filepath.Walk("./roundtrip", func(path string, info os.FileInfo, err error) error { 16 | if !strings.HasPrefix(info.Name(), "v4") { 17 | return nil 18 | } 19 | t.Logf("test roundtrip for %s", path) 20 | return testLookupRoundTrip(t, path) 21 | }) 22 | if err != nil { 23 | t.Error(err) 24 | } 25 | } 26 | 27 | func testLookupRoundTrip(t *testing.T, path string) error { 28 | f, err := os.Open(path) 29 | if err != nil { 30 | return err 31 | } 32 | state, err := tfstate.Read(context.Background(), f) 33 | if err != nil { 34 | return err 35 | } 36 | names, err := state.List() 37 | if err != nil { 38 | return err 39 | } 40 | if len(names) == 0 { 41 | return fmt.Errorf("failed to list resources in %s", path) 42 | } 43 | for _, name := range names { 44 | t.Logf("looking up for %s", name) 45 | res, err := state.Lookup(name) 46 | if err != nil { 47 | return err 48 | } 49 | if res == nil || res.String() == "null" { 50 | return fmt.Errorf("failed to lookup %s in %s", name, path) 51 | } 52 | t.Logf("found %s", res) 53 | } 54 | return nil 55 | } 56 | -------------------------------------------------------------------------------- /tfstate/test/terraform.tfstate: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "terraform_version": "0.12.16", 4 | "serial": 173, 5 | "lineage": "054d7292-3d84-0584-4590-24d6f3b17399", 6 | "outputs": { 7 | "foo": { 8 | "value": "FOO", 9 | "type": "string" 10 | }, 11 | "bar": { 12 | "value": [ 13 | "A", 14 | "B", 15 | "C" 16 | ], 17 | "type": [ 18 | "tuple", 19 | [ 20 | "string", 21 | "string", 22 | "string" 23 | ] 24 | ] 25 | }, 26 | "dash-tuple": { 27 | "value": [ 28 | 3, 29 | 2, 30 | 1 31 | ], 32 | "type": [ 33 | "tuple", 34 | [ 35 | "number", 36 | "number", 37 | "number" 38 | ] 39 | ] 40 | } 41 | }, 42 | "resources": [ 43 | { 44 | "mode": "data", 45 | "type": "terraform_remote_state", 46 | "name": "remote", 47 | "provider": "provider[\"terraform.io/builtin/terraform\"]", 48 | "instances": [ 49 | { 50 | "schema_version": 0, 51 | "attributes": { 52 | "backend": "local", 53 | "defaults": null, 54 | "outputs": { 55 | "value": { 56 | "kms_key": { 57 | "arn": "arn:aws:kms:ap-northeast-1:123456789012:key/500193e3-ddd9-4581-ab0c-fd7aeaedf3e1", 58 | "customer_master_key_spec": "SYMMETRIC_DEFAULT", 59 | "deletion_window_in_days": null, 60 | "description": "my kms key", 61 | "enable_key_rotation": false, 62 | "id": "500193e3-ddd9-4581-ab0c-fd7aeaedf3e1", 63 | "is_enabled": true, 64 | "key_id": "500193e3-ddd9-4581-ab0c-fd7aeaedf3e1", 65 | "key_usage": "ENCRYPT_DECRYPT", 66 | "policy": "{}", 67 | "tags": { 68 | "env": "remote" 69 | } 70 | }, 71 | "kms_key_arn": "arn:aws:kms:ap-northeast-1:123456789012:key/500193e3-ddd9-4581-ab0c-fd7aeaedf3e1", 72 | "mylist": [ 73 | 1, 74 | 2, 75 | 3 76 | ] 77 | }, 78 | "type": [ 79 | "object", 80 | { 81 | "kms_key": [ 82 | "object", 83 | { 84 | "arn": "string", 85 | "customer_master_key_spec": "string", 86 | "deletion_window_in_days": "number", 87 | "description": "string", 88 | "enable_key_rotation": "bool", 89 | "id": "string", 90 | "is_enabled": "bool", 91 | "key_id": "string", 92 | "key_usage": "string", 93 | "policy": "string", 94 | "tags": [ 95 | "map", 96 | "string" 97 | ] 98 | } 99 | ], 100 | "kms_key_arn": "string", 101 | "mylist": [ 102 | "tuple", 103 | [ 104 | "number", 105 | "number", 106 | "number" 107 | ] 108 | ] 109 | } 110 | ] 111 | }, 112 | "workspace": null 113 | }, 114 | "sensitive_attributes": [] 115 | } 116 | ] 117 | }, 118 | { 119 | "mode": "data", 120 | "type": "aws_caller_identity", 121 | "name": "current", 122 | "provider": "provider.aws", 123 | "instances": [ 124 | { 125 | "schema_version": 0, 126 | "attributes": { 127 | "account_id": "123456789012", 128 | "arn": "arn:aws:sts::123456789012:assumed-role/foo/1583745336520988000", 129 | "id": "2020-03-09 09:16:00.754235 +0000 UTC", 130 | "user_id": "xxxxxxx:1583745336520988000" 131 | } 132 | } 133 | ] 134 | }, 135 | { 136 | "mode": "managed", 137 | "type": "aws_acm_certificate", 138 | "name": "main", 139 | "provider": "provider.aws", 140 | "instances": [ 141 | { 142 | "attributes": { 143 | "arn": "arn:aws:acm:ap-northeast-1:123456789012:certificate/4986a36e-7027-4265-864b-1fe32f96d774", 144 | "certificate_authority_arn": "", 145 | "certificate_body": null, 146 | "certificate_chain": null, 147 | "domain_name": "example.com", 148 | "domain_validation_options": [ 149 | { 150 | "domain_name": "example.com", 151 | "resource_record_name": "_6a89c060c6b487799746d5764bfaf659.example.com.", 152 | "resource_record_type": "CNAME", 153 | "resource_record_value": "_2c68f3a12a12fd1c8504175708b95a72.abcdefg.acm-validations.aws." 154 | }, 155 | { 156 | "domain_name": "*.example.com", 157 | "resource_record_name": "_6a89c060c6b487799746d5764bfaf659.example.com.", 158 | "resource_record_type": "CNAME", 159 | "resource_record_value": "_2c68f3a12a12fd1c8504175708b95a72.abcdefg.acm-validations.aws." 160 | } 161 | ], 162 | "id": "arn:aws:acm:ap-northeast-1:123456789012:certificate/8f10da5e-d173-4891-a9d3-1309fa6ff4f4", 163 | "options": [ 164 | { 165 | "certificate_transparency_logging_preference": "ENABLED" 166 | } 167 | ], 168 | "private_key": null, 169 | "subject_alternative_names": [ 170 | "*.example.com" 171 | ], 172 | "tags": { 173 | "env": "world" 174 | }, 175 | "validation_emails": [], 176 | "validation_method": "DNS" 177 | }, 178 | "private": "bnVsbA==" 179 | } 180 | ] 181 | }, 182 | { 183 | "module": "module.logs", 184 | "mode": "managed", 185 | "type": "aws_cloudwatch_log_group", 186 | "name": "main", 187 | "each": "map", 188 | "provider": "provider.aws", 189 | "instances": [ 190 | { 191 | "schema_version": 0, 192 | "attributes": { 193 | "arn": "arn:aws:logs:ap-northeast-1:123456789012:log-group:/main/vanish:*", 194 | "id": "/main/vanish", 195 | "kms_key_id": "", 196 | "name": "/main/vanish", 197 | "name_prefix": null, 198 | "retention_in_days": 30, 199 | "tags": { 200 | "env": "world" 201 | } 202 | }, 203 | "private": "bnVsbA==" 204 | }, 205 | { 206 | "index_key": "app", 207 | "schema_version": 0, 208 | "attributes": { 209 | "arn": "arn:aws:logs:ap-northeast-1:123456789012:log-group:/main/app:*", 210 | "id": "/main/app", 211 | "kms_key_id": "", 212 | "name": "/main/app", 213 | "name_prefix": null, 214 | "retention_in_days": 30, 215 | "tags": { 216 | "env": "world" 217 | } 218 | }, 219 | "private": "bnVsbA==" 220 | }, 221 | { 222 | "index_key": "web", 223 | "schema_version": 0, 224 | "attributes": { 225 | "arn": "arn:aws:logs:ap-northeast-1:123456789012:log-group:/main/web:*", 226 | "id": "/main/web", 227 | "kms_key_id": "", 228 | "name": "/main/web", 229 | "name_prefix": null, 230 | "retention_in_days": 30, 231 | "tags": { 232 | "env": "world" 233 | } 234 | }, 235 | "private": "bnVsbA==" 236 | } 237 | ] 238 | }, 239 | { 240 | "mode": "managed", 241 | "type": "aws_iam_role_policy_attachment", 242 | "name": "ec2", 243 | "each": "list", 244 | "provider": "provider.aws", 245 | "instances": [ 246 | { 247 | "index_key": 0, 248 | "schema_version": 0, 249 | "attributes": { 250 | "id": "ec2-20190801065413533200000002", 251 | "policy_arn": "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess", 252 | "role": "ec2" 253 | }, 254 | "private": "bnVsbA==" 255 | }, 256 | { 257 | "index_key": 1, 258 | "schema_version": 0, 259 | "attributes": { 260 | "id": "ec2-20190801065413531100000001", 261 | "policy_arn": "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser", 262 | "role": "ec2" 263 | }, 264 | "private": "bnVsbA==" 265 | } 266 | ] 267 | }, 268 | { 269 | "module": "module.webapp.module.ecs_task_roles", 270 | "mode": "managed", 271 | "type": "aws_iam_role", 272 | "name": "task_execution_role", 273 | "provider": "provider.aws", 274 | "instances": [ 275 | { 276 | "schema_version": 0, 277 | "attributes": { 278 | "arn": "arn:aws:iam::123456789012:role/task-execution-role", 279 | "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"ecs-tasks.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}", 280 | "create_date": "2019-11-18T07:57:06Z", 281 | "description": "", 282 | "force_detach_policies": false, 283 | "id": "task-execution-role", 284 | "max_session_duration": 3600, 285 | "name": "task-execution-role", 286 | "name_prefix": null, 287 | "path": "/", 288 | "permissions_boundary": null, 289 | "tags": {}, 290 | "unique_id": "AAAAAAAAAAAA" 291 | }, 292 | "private": "bnVsbA==" 293 | } 294 | ] 295 | }, 296 | { 297 | "module": "module.subnets", 298 | "mode": "managed", 299 | "type": "aws_subnet", 300 | "name": "main", 301 | "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", 302 | "instances": [ 303 | { 304 | "index_key": 0, 305 | "schema_version": 1, 306 | "attributes": { 307 | "arn": "arn:aws:ec2:ap-northeast-1:123456789012:subnet/subnet-01234567890123456", 308 | "assign_ipv6_address_on_creation": false, 309 | "availability_zone": "ap-northeast-1a", 310 | "availability_zone_id": "apne1-az4", 311 | "cidr_block": "10.11.12.0/22", 312 | "id": "subnet-01234567890123456", 313 | "ipv6_cidr_block": "", 314 | "ipv6_cidr_block_association_id": "", 315 | "map_public_ip_on_launch": false, 316 | "outpost_arn": "", 317 | "owner_id": "012345678901", 318 | "tags": {}, 319 | "timeouts": null, 320 | "vpc_id": "vpc-01234567" 321 | }, 322 | "private": "bnVsbA==" 323 | }, 324 | { 325 | "index_key": 1, 326 | "schema_version": 1, 327 | "attributes": { 328 | "arn": "arn:aws:ec2:ap-northeast-1:123456789012:subnet/subnet-90123456789012345", 329 | "assign_ipv6_address_on_creation": false, 330 | "availability_zone": "ap-northeast-1a", 331 | "availability_zone_id": "apne1-az4", 332 | "cidr_block": "10.11.15.0/22", 333 | "id": "subnet-90123456789012345", 334 | "ipv6_cidr_block": "", 335 | "ipv6_cidr_block_association_id": "", 336 | "map_public_ip_on_launch": false, 337 | "outpost_arn": "", 338 | "owner_id": "012345678901", 339 | "tags": {}, 340 | "timeouts": null, 341 | "vpc_id": "vpc-01234567" 342 | }, 343 | "private": "bnVsbA==" 344 | } 345 | ] 346 | }, 347 | { 348 | "mode": "managed", 349 | "type": "aws_iam_user", 350 | "name": "users", 351 | "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", 352 | "instances": [ 353 | { 354 | "index_key": "foo.bar", 355 | "schema_version": 0, 356 | "attributes": { 357 | "arn": "arn:aws:iam::xxxxxxxxxxxx:user/foo.bar", 358 | "force_destroy": false, 359 | "id": "foo.bar", 360 | "name": "foo.bar", 361 | "path": "/", 362 | "permissions_boundary": null, 363 | "tags": {}, 364 | "unique_id": "XXXXXXXXXXXXXXXXXXXXX" 365 | }, 366 | "private": "bnVsbA==" 367 | }, 368 | { 369 | "index_key": "hoge.fuga", 370 | "schema_version": 0, 371 | "attributes": { 372 | "arn": "arn:aws:iam::xxxxxxxxxxxx:user/hoge.fuga", 373 | "force_destroy": false, 374 | "id": "hoge.fuga", 375 | "name": "hoge.fuga", 376 | "path": "/", 377 | "permissions_boundary": null, 378 | "tags": {}, 379 | "unique_id": "XXXXXXXXXXXXXXXXXXXXX" 380 | }, 381 | "private": "bnVsbA==" 382 | } 383 | ] 384 | }, 385 | { 386 | "mode": "managed", 387 | "type": "aws_iam_user", 388 | "name": "user", 389 | "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", 390 | "instances": [ 391 | { 392 | "index_key": "me", 393 | "schema_version": 0, 394 | "attributes": { 395 | "arn": "arn:aws:iam::xxxxxxxxxxxx:user/me", 396 | "force_destroy": false, 397 | "id": "me", 398 | "name": "me", 399 | "path": "/", 400 | "permissions_boundary": null, 401 | "tags": {}, 402 | "unique_id": "XXXXXXXXXXXXXXXXXXXXX" 403 | }, 404 | "private": "bnVsbA==" 405 | } 406 | ] 407 | }, 408 | { 409 | "mode": "data", 410 | "type": "aws_lb_target_group", 411 | "name": "app", 412 | "each": "map", 413 | "provider": "provider.aws", 414 | "instances": [ 415 | { 416 | "index_key": "dev1", 417 | "schema_version": 0, 418 | "attributes": { 419 | "arn": "arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/dev-dev1-app/d122c303215d2e0c", 420 | "arn_suffix": "targetgroup/dev-dev1-app/d122c303215d2e0c", 421 | "deregistration_delay": 300, 422 | "health_check": [ 423 | { 424 | "enabled": true, 425 | "healthy_threshold": 2, 426 | "interval": 5, 427 | "matcher": "", 428 | "path": "/AWS.ALB/healthcheck", 429 | "port": "traffic-port", 430 | "protocol": "HTTP", 431 | "timeout": 3, 432 | "unhealthy_threshold": 10 433 | } 434 | ], 435 | "id": "arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/dev-dev1-app/d122c303215d2e0c", 436 | "lambda_multi_value_headers_enabled": null, 437 | "load_balancing_algorithm_type": "round_robin", 438 | "name": "dev-dev1-app", 439 | "port": 8080, 440 | "protocol": "HTTP", 441 | "proxy_protocol_v2": null, 442 | "slow_start": 0, 443 | "stickiness": [ 444 | { 445 | "cookie_duration": 86400, 446 | "enabled": false, 447 | "type": "lb_cookie" 448 | } 449 | ], 450 | "tags": {}, 451 | "target_type": "ip", 452 | "vpc_id": "vpc-00435ca4ee84a4fb9" 453 | } 454 | }, 455 | { 456 | "index_key": "dev2", 457 | "schema_version": 0, 458 | "attributes": { 459 | "arn": "arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/dev-dev2-app/3c09e9a755f72d26", 460 | "arn_suffix": "targetgroup/dev-dev2-app/3c09e9a755f72d26", 461 | "deregistration_delay": 300, 462 | "health_check": [ 463 | { 464 | "enabled": true, 465 | "healthy_threshold": 2, 466 | "interval": 5, 467 | "matcher": "", 468 | "path": "/AWS.ALB/healthcheck", 469 | "port": "traffic-port", 470 | "protocol": "HTTP", 471 | "timeout": 3, 472 | "unhealthy_threshold": 10 473 | } 474 | ], 475 | "id": "arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/dev-dev2-app/3c09e9a755f72d26", 476 | "lambda_multi_value_headers_enabled": null, 477 | "load_balancing_algorithm_type": "round_robin", 478 | "name": "dev-dev2-app", 479 | "port": 8080, 480 | "protocol": "HTTP", 481 | "proxy_protocol_v2": null, 482 | "slow_start": 0, 483 | "stickiness": [ 484 | { 485 | "cookie_duration": 86400, 486 | "enabled": false, 487 | "type": "lb_cookie" 488 | } 489 | ], 490 | "tags": {}, 491 | "target_type": "ip", 492 | "vpc_id": "vpc-00435ca4ee84a4fb9" 493 | } 494 | }, 495 | { 496 | "index_key": "dev3", 497 | "schema_version": 0, 498 | "attributes": { 499 | "arn": "arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/dev-dev3-app/aa3a78fa94b2b463", 500 | "arn_suffix": "targetgroup/dev-dev3-app/aa3a78fa94b2b463", 501 | "deregistration_delay": 300, 502 | "health_check": [ 503 | { 504 | "enabled": true, 505 | "healthy_threshold": 2, 506 | "interval": 5, 507 | "matcher": "", 508 | "path": "/AWS.ALB/healthcheck", 509 | "port": "traffic-port", 510 | "protocol": "HTTP", 511 | "timeout": 3, 512 | "unhealthy_threshold": 10 513 | } 514 | ], 515 | "id": "arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/dev-dev3-app/aa3a78fa94b2b463", 516 | "lambda_multi_value_headers_enabled": null, 517 | "load_balancing_algorithm_type": "round_robin", 518 | "name": "dev-dev3-app", 519 | "port": 8080, 520 | "protocol": "HTTP", 521 | "proxy_protocol_v2": null, 522 | "slow_start": 0, 523 | "stickiness": [ 524 | { 525 | "cookie_duration": 86400, 526 | "enabled": false, 527 | "type": "lb_cookie" 528 | } 529 | ], 530 | "tags": {}, 531 | "target_type": "ip", 532 | "vpc_id": "vpc-00435ca4ee84a4fb9" 533 | } 534 | } 535 | ] 536 | }, 537 | { 538 | "module": "module.example", 539 | "mode": "managed", 540 | "type": "aws_vpc", 541 | "name": "example", 542 | "provider": "provider.aws", 543 | "instances": [] 544 | }, 545 | { 546 | "mode": "data", 547 | "type": "terraform_remote_state", 548 | "name": "hyphenated-id", 549 | "provider": "provider[\"terraform.io/builtin/terraform\"]", 550 | "instances": [ 551 | { 552 | "schema_version": 0, 553 | "attributes": { 554 | "backend": "local", 555 | "defaults": null, 556 | "outputs": { 557 | "value": { 558 | "repository-uri": "123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/app" 559 | }, 560 | "type": [ 561 | "object", 562 | { 563 | "repository-uri": "string" 564 | } 565 | ] 566 | }, 567 | "workspace": null 568 | }, 569 | "sensitive_attributes": [] 570 | } 571 | ] 572 | } 573 | ] 574 | } 575 | --------------------------------------------------------------------------------