diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3e841ca4e..6141ff32a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: name: lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 - uses: ./.github/actions/lint-provider-tfe tests: @@ -35,7 +35,7 @@ jobs: organization: hashicorp-v2 workspace: tflocal-terraform-provider-tfe - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 - uses: ./.github/actions/test-provider-tfe with: @@ -76,7 +76,7 @@ jobs: run: jrm ./ci-summary-provider.xml "junit-test-summary-0/*.xml" "junit-test-summary-1/*.xml" "junit-test-summary-2/*.xml" "junit-test-summary-3/*.xml" "junit-test-summary-4/*.xml" - name: Upload test artifacts - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + uses: actions/upload-artifact@1746f4ab65b179e0ea60a494b83293b640dd5bba # v4.3.2 with: name: junit-test-summary path: ./ci-summary-provider.xml diff --git a/.github/workflows/create-release-pr.yml b/.github/workflows/create-release-pr.yml index 53694e3c0..d42655811 100644 --- a/.github/workflows/create-release-pr.yml +++ b/.github/workflows/create-release-pr.yml @@ -17,7 +17,7 @@ jobs: outputs: version: ${{ steps.remove-leading-v.outputs.version }} steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 - uses: actions-ecosystem/action-get-latest-tag@b7c32daec3395a9616f88548363a42652b22d435 # v1.6.0 id: get-latest-tag @@ -33,7 +33,7 @@ jobs: buildBinary: runs-on: ubuntu-latest steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 - name: Set up Go uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 with: @@ -51,7 +51,7 @@ jobs: go build -o terraform-provider-tfe - name: Upload test artifacts - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + uses: actions/upload-artifact@1746f4ab65b179e0ea60a494b83293b640dd5bba # v4.3.2 with: name: terraform-provider-tfe path: terraform-provider-tfe @@ -68,7 +68,7 @@ jobs: CHECKPOINT_DISABLE: "1" timeout-minutes: 120 steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 - run: git config --global user.email "bcroft@hashicorp.com" - run: git config --global user.name "Brandon Croft" - name: Get yarn cache directory path @@ -102,7 +102,7 @@ jobs: TF_PLUGIN_CACHE_DIR: ${{ steps.global-cache-dir-path.outputs.dir }}/terraform-plugins - name: Create Pull Request - uses: peter-evans/create-pull-request@70a41aba780001da0a30141984ae2a0c95d8704e # v6.0.2 + uses: peter-evans/create-pull-request@9153d834b60caba6d51c9b9510b087acf9f33f83 # v6.0.4 with: commit-message: "cdktf: update documentation" title: "release: ${{ github.event.inputs.version }}" diff --git a/.github/workflows/mkdocs.yml b/.github/workflows/mkdocs.yml index aeb2998af..c63b6591b 100644 --- a/.github/workflows/mkdocs.yml +++ b/.github/workflows/mkdocs.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout main - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 - name: Deploy docs uses: mhausenblas/mkdocs-deploy-gh-pages@d77dd03172e96abbcdb081d8c948224762033653 # 1.26 diff --git a/.github/workflows/nightly-tfe-test.yml b/.github/workflows/nightly-tfe-test.yml index e4495da06..495b90ffb 100644 --- a/.github/workflows/nightly-tfe-test.yml +++ b/.github/workflows/nightly-tfe-test.yml @@ -36,7 +36,7 @@ jobs: organization: hashicorp-v2 workspace: tflocal-terraform-provider-tfe-nightly - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 - uses: ./.github/actions/test-provider-tfe with: @@ -65,7 +65,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Send slack notification on failure - uses: slackapi/slack-github-action@6c661ce58804a1a20f6dc5fbee7f0381b469e001 # v1.25.0 + uses: slackapi/slack-github-action@70cd7be8e40a46e8b0eced40b0de447bdb42f68e # v1.26.0 with: payload: | { @@ -96,7 +96,7 @@ jobs: needs: [tests-summarize] if: "${{ always() }}" steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 - name: Set up Go uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e45101d3f..c4bc31d14 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,12 +12,12 @@ jobs: release-notes: runs-on: ubuntu-latest steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 with: fetch-depth: 0 - name: Generate Release Notes run: sed -n -e "1{/# /d;}" -e "2{/^$/d;}" -e "/# $(git describe --abbrev=0 --exclude="$(git describe --abbrev=0 --match='v*.*.*' --tags)" --match='v*.*.*' --tags | tr -d v)/q;p" CHANGELOG.md > release-notes.txt - - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + - uses: actions/upload-artifact@1746f4ab65b179e0ea60a494b83293b640dd5bba # v4.3.2 with: name: release-notes path: release-notes.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 803ced202..bbd82b07d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,11 @@ * Adds `post_apply` to list of possible `stages` for Run Tasks by @carolinaborim [#1307](https://github.com/hashicorp/terraform-provider-tfe/pull/1307) ### Features +* `d/tfe_oauth_client`: Add `project_ids` attribute, by @Netra2104 [1148](https://github.com/hashicorp/terraform-provider-tfe/pull/1148) +* `d/tfe_oauth_client`: Add `organization_scoped` attribute, by @Netra2104 [1148](https://github.com/hashicorp/terraform-provider-tfe/pull/1148) * **New Resource**: `r/tfe_project_oauth_client` attaches/detaches an existing `project` to an existing `oauth client`, by @Netra2104 [1144](https://github.com/hashicorp/terraform-provider-tfe/pull/1144) - +* **New Resource**: `r/tfe_test_variable` is a new resource for creating environment variables used by registry modules for terraform test, by @aaabdelgany [1285](https://github.com/hashicorp/terraform-provider-tfe/pull/1285) + ## v0.53.0 ENHANCEMENTS: diff --git a/META.d/_summary.yaml b/META.d/_summary.yaml new file mode 100644 index 000000000..4bdec6a9d --- /dev/null +++ b/META.d/_summary.yaml @@ -0,0 +1,10 @@ +--- + +schema: 1.1 +partition: tfc +category: library + +summary: + owner: team-tf-cli + description: Official Terraform Cloud/Enterprise provider, maintained by HashiCorp. Provision Terraform Cloud or Terraform Enterprise - with Terraform! + visibility: external diff --git a/go.mod b/go.mod index bb59f73ec..be30a5156 100644 --- a/go.mod +++ b/go.mod @@ -5,30 +5,30 @@ go 1.21 require ( github.com/agext/levenshtein v1.2.3 // indirect github.com/fatih/color v1.16.0 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-hclog v1.5.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-retryablehttp v0.7.5 // indirect - github.com/hashicorp/go-slug v0.14.0 + github.com/hashicorp/go-slug v0.15.0 github.com/hashicorp/go-tfe v1.49.0 github.com/hashicorp/go-version v1.6.0 github.com/hashicorp/hcl v1.0.0 github.com/hashicorp/hcl/v2 v2.19.1 // indirect - github.com/hashicorp/terraform-plugin-framework v1.7.0 + github.com/hashicorp/terraform-plugin-framework v1.8.0 github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 - github.com/hashicorp/terraform-plugin-go v0.22.1 + github.com/hashicorp/terraform-plugin-go v0.22.2 github.com/hashicorp/terraform-plugin-mux v0.15.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0 github.com/hashicorp/terraform-svchost v0.1.1 github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/zclconf/go-cty v1.14.4 - golang.org/x/crypto v0.19.0 // indirect - golang.org/x/net v0.20.0 // indirect - golang.org/x/oauth2 v0.16.0 // indirect - golang.org/x/sys v0.17.0 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/net v0.23.0 // indirect + golang.org/x/oauth2 v0.17.0 // indirect + golang.org/x/sys v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect google.golang.org/protobuf v1.33.0 // indirect @@ -56,7 +56,7 @@ require ( github.com/oklog/run v1.1.0 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/grpc v1.62.1 // indirect + google.golang.org/grpc v1.63.2 // indirect ) require ( @@ -77,7 +77,7 @@ require ( github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect golang.org/x/mod v0.15.0 // indirect golang.org/x/sync v0.6.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index faf721608..c5a424e19 100644 --- a/go.sum +++ b/go.sum @@ -39,8 +39,8 @@ github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+Licev github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -67,10 +67,8 @@ github.com/hashicorp/go-plugin v1.6.0 h1:wgd4KxHJTVGGqWBq4QPB1i5BZNEx9BR8+OFmHDm github.com/hashicorp/go-plugin v1.6.0/go.mod h1:lBS5MtSSBZk0SHc66KACcjjlU6WzEVP/8pwz68aMkCI= github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M= github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= -github.com/hashicorp/go-slug v0.14.0 h1:/aZdUDjR74TSlsQp3hA9nqhCQkQHAUr2jjtuUfWqI9E= -github.com/hashicorp/go-slug v0.14.0/go.mod h1:THWVTAXwJEinbsp4/bBRcmbaO5EYNLTqxbG4tZ3gCYQ= -github.com/hashicorp/go-tfe v1.47.1 h1:+DkqcPK6RGEsCC34vFau8/yOky4ST84o8AFdB6NbrJg= -github.com/hashicorp/go-tfe v1.47.1/go.mod h1:5nP2G4S/uD8JIm3JEcM+1oLElOUgQtiXUhRQ5+DGaNg= +github.com/hashicorp/go-slug v0.15.0 h1:AhMnE6JIyW0KoDJlmRDwv4xd52a5ZK3VdioQ7SMmZhI= +github.com/hashicorp/go-slug v0.15.0/go.mod h1:THWVTAXwJEinbsp4/bBRcmbaO5EYNLTqxbG4tZ3gCYQ= github.com/hashicorp/go-tfe v1.49.0 h1:HIxG/fwjXoZ5M4EQZcZkgYFCJkWtbsT9T/x71HQr5kw= github.com/hashicorp/go-tfe v1.49.0/go.mod h1:5nP2G4S/uD8JIm3JEcM+1oLElOUgQtiXUhRQ5+DGaNg= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= @@ -92,12 +90,12 @@ github.com/hashicorp/terraform-exec v0.20.0 h1:DIZnPsqzPGuUnq6cH8jWcPunBfY+C+M8J github.com/hashicorp/terraform-exec v0.20.0/go.mod h1:ckKGkJWbsNqFKV1itgMnE0hY9IYf1HoiekpuN0eWoDw= github.com/hashicorp/terraform-json v0.21.0 h1:9NQxbLNqPbEMze+S6+YluEdXgJmhQykRyRNd+zTI05U= github.com/hashicorp/terraform-json v0.21.0/go.mod h1:qdeBs11ovMzo5puhrRibdD6d2Dq6TyE/28JiU4tIQxk= -github.com/hashicorp/terraform-plugin-framework v1.7.0 h1:wOULbVmfONnJo9iq7/q+iBOBJul5vRovaYJIu2cY/Pw= -github.com/hashicorp/terraform-plugin-framework v1.7.0/go.mod h1:jY9Id+3KbZ17OMpulgnWLSfwxNVYSoYBQFTgsx044CI= +github.com/hashicorp/terraform-plugin-framework v1.8.0 h1:P07qy8RKLcoBkCrY2RHJer5AEvJnDuXomBgou6fD8kI= +github.com/hashicorp/terraform-plugin-framework v1.8.0/go.mod h1:/CpTukO88PcL/62noU7cuyaSJ4Rsim+A/pa+3rUVufY= github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 h1:HOjBuMbOEzl7snOdOoUfE2Jgeto6JOjLVQ39Ls2nksc= github.com/hashicorp/terraform-plugin-framework-validators v0.12.0/go.mod h1:jfHGE/gzjxYz6XoUwi/aYiiKrJDeutQNUtGQXkaHklg= -github.com/hashicorp/terraform-plugin-go v0.22.1 h1:iTS7WHNVrn7uhe3cojtvWWn83cm2Z6ryIUDTRO0EV7w= -github.com/hashicorp/terraform-plugin-go v0.22.1/go.mod h1:qrjnqRghvQ6KnDbB12XeZ4FluclYwptntoWCr9QaXTI= +github.com/hashicorp/terraform-plugin-go v0.22.2 h1:5o8uveu6eZUf5J7xGPV0eY0TPXg3qpmwX9sce03Bxnc= +github.com/hashicorp/terraform-plugin-go v0.22.2/go.mod h1:drq8Snexp9HsbFZddvyLHN6LuWHHndSQg+gV+FPkcIM= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= github.com/hashicorp/terraform-plugin-mux v0.15.0 h1:+/+lDx0WUsIOpkAmdwBIoFU8UP9o2eZASoOnLsWbKME= @@ -175,8 +173,8 @@ github.com/zclconf/go-cty v1.14.4/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgr golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= @@ -186,10 +184,10 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= -golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= +golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -211,8 +209,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -237,10 +235,10 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= -google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= -google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= +google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= +google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= diff --git a/internal/provider/data_source_oauth_client.go b/internal/provider/data_source_oauth_client.go index 5f977dca2..6c4f92cf2 100644 --- a/internal/provider/data_source_oauth_client.go +++ b/internal/provider/data_source_oauth_client.go @@ -80,6 +80,15 @@ func dataSourceTFEOAuthClient() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "organization_scoped": { + Type: schema.TypeBool, + Computed: true, + }, + "project_ids": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + }, }, } } @@ -132,6 +141,7 @@ func dataSourceTFEOAuthClientRead(d *schema.ResourceData, meta interface{}) erro } d.Set("service_provider", oc.ServiceProvider) d.Set("service_provider_display_name", oc.ServiceProviderName) + d.Set("organization_scoped", oc.OrganizationScoped) switch len(oc.OAuthTokens) { case 0: @@ -142,5 +152,11 @@ func dataSourceTFEOAuthClientRead(d *schema.ResourceData, meta interface{}) erro return fmt.Errorf("unexpected number of OAuth tokens: %d", len(oc.OAuthTokens)) } + var projectIDs []interface{} + for _, project := range oc.Projects { + projectIDs = append(projectIDs, project.ID) + } + d.Set("project_ids", projectIDs) + return nil } diff --git a/internal/provider/data_source_oauth_client_test.go b/internal/provider/data_source_oauth_client_test.go index b9df39804..562289096 100644 --- a/internal/provider/data_source_oauth_client_test.go +++ b/internal/provider/data_source_oauth_client_test.go @@ -20,6 +20,28 @@ func testAccTFEOAuthClientDataSourcePreCheck(t *testing.T) { } } +func TestAccTFEOAuthClientDataSource_basic(t *testing.T) { + rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccTFEOAuthClientDataSourcePreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccTFEOAuthClientDataSourceConfig_basic(rInt), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet("data.tfe_oauth_client.client", "id"), + resource.TestCheckResourceAttr( + "data.tfe_oauth_client.client", "organization_scoped", "true"), + resource.TestCheckResourceAttr( + "data.tfe_oauth_client.client", "project_ids.#", "1"), + ), + }, + }, + }, + ) +} + func TestAccTFEOAuthClientDataSource_findByID(t *testing.T) { rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() resource.Test(t, resource.TestCase{ @@ -41,6 +63,9 @@ func TestAccTFEOAuthClientDataSource_findByID(t *testing.T) { resource.TestCheckResourceAttrPair( "tfe_oauth_client.test", "oauth_token_id", "data.tfe_oauth_client.client", "oauth_token_id"), + resource.TestCheckResourceAttrPair( + "tfe_oauth_client.test", "organization_scoped", + "data.tfe_oauth_client.client", "organization_scoped"), ), }, }, @@ -68,6 +93,9 @@ func TestAccTFEOAuthClientDataSource_findByName(t *testing.T) { resource.TestCheckResourceAttrPair( "tfe_oauth_client.test", "oauth_token_id", "data.tfe_oauth_client.client", "oauth_token_id"), + resource.TestCheckResourceAttrPair( + "tfe_oauth_client.test", "organization_scoped", + "data.tfe_oauth_client.client", "organization_scoped"), ), }, }, @@ -185,6 +213,41 @@ func TestAccTFEOAuthClientDataSource_sameServiceProvider(t *testing.T) { }) } +func testAccTFEOAuthClientDataSourceConfig_basic(rInt int) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_project" "foobar" { + name = "project-foo-%d" + organization = tfe_organization.foobar.name +} + +resource "tfe_oauth_client" "test" { + organization = tfe_organization.foobar.name + api_url = "https://api.github.com" + http_url = "https://github.com" + oauth_token = "%s" + service_provider = "github" + organization_scoped = true +} + +resource "tfe_project_oauth_client" "foobar" { + oauth_client_id = tfe_oauth_client.test.id + project_id = tfe_project.foobar.id +} + +data "tfe_oauth_client" "client" { + name = tfe_oauth_client.test.name + organization = tfe_organization.foobar.name + oauth_client_id = tfe_oauth_client.test.id + depends_on=[tfe_project_oauth_client.foobar] +} +`, rInt, rInt, envGithubToken) +} + func testAccTFEOAuthClientDataSourceConfig_findByID(rInt int) string { return fmt.Sprintf(` resource "tfe_organization" "foobar" { @@ -197,6 +260,7 @@ resource "tfe_oauth_client" "test" { http_url = "https://github.com" oauth_token = "%s" service_provider = "github" + organization_scoped = true } data "tfe_oauth_client" "client" { oauth_client_id = tfe_oauth_client.test.id @@ -217,6 +281,7 @@ resource "tfe_oauth_client" "test" { name = "tst-github-%d" oauth_token = "%s" service_provider = "github" + organization_scoped = true } data "tfe_oauth_client" "client" { organization = "tst-terraform-%d" diff --git a/internal/provider/provider_next.go b/internal/provider/provider_next.go index 1010f00c7..83e74b573 100644 --- a/internal/provider/provider_next.go +++ b/internal/provider/provider_next.go @@ -142,6 +142,7 @@ func (p *frameworkProvider) Resources(ctx context.Context) []func() resource.Res NewResourceVariable, NewResourceWorkspaceSettings, NewSAMLSettingsResource, + NewTestVariableResource, NewWorkspaceRunTaskResource, } } diff --git a/internal/provider/resource_tfe_test_variable.go b/internal/provider/resource_tfe_test_variable.go new file mode 100644 index 000000000..a4e91d8ee --- /dev/null +++ b/internal/provider/resource_tfe_test_variable.go @@ -0,0 +1,358 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package provider + +import ( + "context" + "errors" + "fmt" + "log" + + tfe "github.com/hashicorp/go-tfe" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" +) + +type resourceTFETestVariable struct { + config ConfiguredClient +} + +func NewTestVariableResource() resource.Resource { + return &resourceTFETestVariable{} +} + +// modelTFETestVariable maps the resource schema data to a struct. +type modelTFETestVariable struct { + ID types.String `tfsdk:"id"` + Key types.String `tfsdk:"key"` + Value types.String `tfsdk:"value"` + ReadableValue types.String `tfsdk:"readable_value"` + Category types.String `tfsdk:"category"` + Description types.String `tfsdk:"description"` + HCL types.Bool `tfsdk:"hcl"` + Sensitive types.Bool `tfsdk:"sensitive"` + Organization types.String `tfsdk:"organization"` + ModuleName types.String `tfsdk:"module_name"` + ModuleProvider types.String `tfsdk:"module_provider"` +} + +// modelFromTFETestVariable builds a modelTFETestVariable struct from a tfe.TestVariable +// value (plus the last known value of the variable's `value` attribute). +func modelFromTFETestVariable(v tfe.Variable, lastValue types.String, moduleID tfe.RegistryModuleID) modelTFETestVariable { + // Initialize all fields from the provided API struct + m := modelTFETestVariable{ + ID: types.StringValue(v.ID), + Key: types.StringValue(v.Key), + Value: types.StringValue(v.Value), + Category: types.StringValue(string(v.Category)), + Description: types.StringValue(v.Description), + HCL: types.BoolValue(v.HCL), + Sensitive: types.BoolValue(v.Sensitive), + Organization: types.StringValue(moduleID.Organization), + ModuleName: types.StringValue(moduleID.Name), + ModuleProvider: types.StringValue(moduleID.Provider), + } + // BUT: if the variable is sensitive, carry forward the last known value + // instead, because the API never lets us read it again. + if v.Sensitive { + m.Value = lastValue + m.ReadableValue = types.StringNull() + } else { + m.ReadableValue = m.Value + } + return m +} + +// Configure implements resource.ResourceWithConfigure +func (r *resourceTFETestVariable) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + // Early exit if provider is unconfigured (i.e. we're only validating config or something) + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(ConfiguredClient) + if !ok { + resp.Diagnostics.AddError( + "Unexpected resource Configure type", + fmt.Sprintf("Expected tfe.ConfiguredClient, got %T. This is a bug in the tfe provider, so please report it on GitHub.", req.ProviderData), + ) + } + r.config = client +} + +// Metadata implements resource.Resource +func (r *resourceTFETestVariable) Metadata(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "tfe_test_variable" +} + +// Schema implements resource.Resource +func (r *resourceTFETestVariable) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + Description: "Service-generated identifier for the variable", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "key": schema.StringAttribute{ + Required: true, + Description: "Name of the variable.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplaceIf( + func(ctx context.Context, req planmodifier.StringRequest, resp *stringplanmodifier.RequiresReplaceIfFuncResponse) { + var stateSensitive types.Bool + diags := req.State.GetAttribute(ctx, path.Root("sensitive"), &stateSensitive) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + if stateSensitive.ValueBool() && req.PlanValue.ValueString() != req.StateValue.ValueString() { + resp.RequiresReplace = true + } + }, + "Force replacement if key changed and sensitive is true", + "Force replacement if key changed and sensitive is true", + ), + }, + }, + "value": schema.StringAttribute{ + Optional: true, + Computed: true, + Default: stringdefault.StaticString(""), + Sensitive: true, + Description: "Value of the variable", + }, + "category": schema.StringAttribute{ + Required: true, + Description: `Whether this is a Terraform or environment variable. Valid values are "terraform" or "env".`, + Validators: []validator.String{ + stringvalidator.OneOf( + string(tfe.CategoryEnv), + ), + }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "description": schema.StringAttribute{ + Optional: true, + Computed: true, + Default: stringdefault.StaticString(""), + }, + "hcl": schema.BoolAttribute{ + Optional: true, + Computed: true, + Default: booldefault.StaticBool(false), + }, + "sensitive": schema.BoolAttribute{ + Optional: true, + Computed: true, + Default: booldefault.StaticBool(false), + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.RequiresReplaceIf( + func(ctx context.Context, req planmodifier.BoolRequest, resp *boolplanmodifier.RequiresReplaceIfFuncResponse) { + if req.StateValue.ValueBool() && !req.ConfigValue.ValueBool() { + resp.RequiresReplace = true + } + }, + "Force replacement if sensitive argument changed from true to false.", + "Force replacement if sensitive argument changed from true to false.", + ), + }, + }, + "organization": schema.StringAttribute{ + Required: true, + }, + "module_name": schema.StringAttribute{ + Required: true, + }, + "module_provider": schema.StringAttribute{ + Required: true, + }, + "readable_value": schema.StringAttribute{ + Computed: true, + Description: "A non-sensitive read-only copy of the variable value, which can be viewed or referenced " + + "in plan outputs without being redacted. Will only be present if the variable is not sensitive", + PlanModifiers: []planmodifier.String{ + &updateReadableValuePlanModifier{}, + }, + }, + }, + Description: "", + MarkdownDescription: "", + DeprecationMessage: "", + Version: 1, + } +} + +func (r *resourceTFETestVariable) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data modelTFETestVariable + diags := req.Plan.Get(ctx, &data) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + key := data.Key.ValueString() + category := data.Category.ValueString() + moduleID := tfe.RegistryModuleID{ + Organization: data.Organization.ValueString(), + Name: data.ModuleName.ValueString(), + Provider: data.ModuleProvider.ValueString(), + Namespace: data.Organization.ValueString(), + RegistryName: "private", + } + + options := tfe.VariableCreateOptions{ + Key: data.Key.ValueStringPointer(), + Value: data.Value.ValueStringPointer(), + Category: tfe.Category(tfe.CategoryType(category)), + HCL: data.HCL.ValueBoolPointer(), + Sensitive: data.Sensitive.ValueBoolPointer(), + Description: data.Description.ValueStringPointer(), + } + + tflog.Debug(ctx, fmt.Sprintf("Create %s variable: %s", category, key)) + variable, err := r.config.Client.TestVariables.Create(ctx, moduleID, options) + if err != nil { + resp.Diagnostics.AddError( + "Error creating variable", + fmt.Sprintf("Couldn't create %s variable %s: %s", category, key, err.Error()), + ) + return + } + + // We got a variable, so set state to new values + result := modelFromTFETestVariable(*variable, data.Value, moduleID) + diags = resp.State.Set(ctx, &result) + resp.Diagnostics.Append(diags...) +} + +func (r *resourceTFETestVariable) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data modelTFETestVariable + + diags := req.State.Get(ctx, &data) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + moduleID := tfe.RegistryModuleID{ + Organization: data.Organization.ValueString(), + Name: data.ModuleName.ValueString(), + Provider: data.ModuleProvider.ValueString(), + Namespace: data.Organization.ValueString(), + RegistryName: "private", + } + + variableID := data.ID.ValueString() + variable, err := r.config.Client.TestVariables.Read(ctx, moduleID, variableID) + if err != nil { + // If it's gone: that's not an error, but we are done. + if errors.Is(err, tfe.ErrResourceNotFound) { + tflog.Debug(ctx, fmt.Sprintf("Variable %s no longer exists", variableID)) + resp.State.RemoveResource(ctx) + } else { + resp.Diagnostics.AddError( + "Error reading variable", + fmt.Sprintf("Couldn't read variable %s: %s", variableID, err.Error()), + ) + } + return + } + + // We got a variable, so update state: + result := modelFromTFETestVariable(*variable, data.Value, moduleID) + diags = resp.State.Set(ctx, &result) + resp.Diagnostics.Append(diags...) +} + +func (r *resourceTFETestVariable) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan modelTFETestVariable + var state modelTFETestVariable + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + diags = req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + variableID := plan.ID.ValueString() + moduleID := tfe.RegistryModuleID{ + Organization: plan.Organization.ValueString(), + Name: plan.ModuleName.ValueString(), + Provider: plan.ModuleProvider.ValueString(), + Namespace: plan.Organization.ValueString(), + RegistryName: "private", + } + + options := tfe.VariableUpdateOptions{ + Key: plan.Key.ValueStringPointer(), + Description: plan.Description.ValueStringPointer(), + HCL: plan.HCL.ValueBoolPointer(), + Sensitive: plan.Sensitive.ValueBoolPointer(), + } + // We ONLY want to set Value if our planned value would be a CHANGE from the + // prior state. See comments in updateWithWorkspace for more color. + if state.Value.ValueString() != plan.Value.ValueString() { + options.Value = plan.Value.ValueStringPointer() + } + + tflog.Debug(ctx, fmt.Sprintf("Update variable: %s", variableID)) + variable, err := r.config.Client.TestVariables.Update(ctx, moduleID, variableID, options) + if err != nil { + resp.Diagnostics.AddError( + "Error updating variable", + fmt.Sprintf("Couldn't update variable %s: %s", variableID, err.Error()), + ) + return + } + // Update state + result := modelFromTFETestVariable(*variable, plan.Value, moduleID) + diags = resp.State.Set(ctx, &result) + resp.Diagnostics.Append(diags...) +} + +func (r *resourceTFETestVariable) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data modelTFETestVariable + diags := req.State.Get(ctx, &data) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + variableID := data.ID.ValueString() + moduleID := tfe.RegistryModuleID{ + Organization: data.Organization.ValueString(), + Name: data.ModuleName.ValueString(), + Provider: data.ModuleProvider.ValueString(), + Namespace: data.Organization.ValueString(), + RegistryName: "private", + } + log.Printf("[DEBUG] Delete variable: %s", variableID) + err := r.config.Client.TestVariables.Delete(ctx, moduleID, variableID) + // Ignore 404s for delete + if err != nil && !errors.Is(err, tfe.ErrResourceNotFound) { + resp.Diagnostics.AddError( + "Error deleting variable", + fmt.Sprintf("Couldn't delete variable %s: %s", variableID, err.Error()), + ) + } + // Resource is implicitly deleted from resp.State if diagnostics have no errors. +} diff --git a/internal/provider/resource_tfe_test_variable_test.go b/internal/provider/resource_tfe_test_variable_test.go new file mode 100644 index 000000000..e66478cee --- /dev/null +++ b/internal/provider/resource_tfe_test_variable_test.go @@ -0,0 +1,286 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package provider + +import ( + "fmt" + "math/rand" + "testing" + "time" + + tfe "github.com/hashicorp/go-tfe" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccTFETestVariable_basic(t *testing.T) { + variable := &tfe.Variable{} + rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccMuxedProviders, + CheckDestroy: testAccCheckTFETestVariableDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTFETestVariable_test_variable(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckTFETestVariableExists( + "tfe_test_variable.foobar", variable), + testAccCheckTFEVariableAttributes(variable), + resource.TestCheckResourceAttr( + "tfe_test_variable.foobar", "key", "key_test"), + resource.TestCheckResourceAttr( + "tfe_test_variable.foobar", "value", "value_test"), + resource.TestCheckResourceAttr( + "tfe_test_variable.foobar", "description", "some description"), + resource.TestCheckResourceAttr( + "tfe_test_variable.foobar", "category", "env"), + resource.TestCheckResourceAttr( + "tfe_test_variable.foobar", "hcl", "false"), + resource.TestCheckResourceAttr( + "tfe_test_variable.foobar", "sensitive", "false"), + ), + }, + }, + }) +} + +func TestAccTFETestVariable_update(t *testing.T) { + variable := &tfe.Variable{} + rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccMuxedProviders, + CheckDestroy: testAccCheckTFETestVariableDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTFETestVariable_test_variable(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckTFETestVariableExists( + "tfe_test_variable.foobar", variable), + testAccCheckTFEVariableAttributes(variable), + resource.TestCheckResourceAttr( + "tfe_test_variable.foobar", "key", "key_test"), + resource.TestCheckResourceAttr( + "tfe_test_variable.foobar", "value", "value_test"), + resource.TestCheckResourceAttr( + "tfe_test_variable.foobar", "description", "some description"), + resource.TestCheckResourceAttr( + "tfe_test_variable.foobar", "category", "env"), + resource.TestCheckResourceAttr( + "tfe_test_variable.foobar", "hcl", "false"), + resource.TestCheckResourceAttr( + "tfe_test_variable.foobar", "sensitive", "false"), + ), + }, + + { + Config: testAccTFETestVariable_update(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckTFETestVariableExists( + "tfe_test_variable.foobar", variable), + testAccCheckTFETestVariableAttributesUpdate(variable), + resource.TestCheckResourceAttr( + "tfe_test_variable.foobar", "key", "key_updated"), + resource.TestCheckResourceAttr( + "tfe_test_variable.foobar", "value", "value_updated"), + resource.TestCheckResourceAttr( + "tfe_test_variable.foobar", "description", "another description"), + resource.TestCheckResourceAttr( + "tfe_test_variable.foobar", "category", "env"), + resource.TestCheckResourceAttr( + "tfe_test_variable.foobar", "hcl", "true"), + resource.TestCheckResourceAttr( + "tfe_test_variable.foobar", "sensitive", "true"), + ), + }, + }, + }) +} + +func testAccCheckTFETestVariableExists( + n string, variable *tfe.Variable) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(ConfiguredClient) + + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No instance ID is set") + } + moduleID := tfe.RegistryModuleID{ + Organization: rs.Primary.Attributes["organization"], + Name: rs.Primary.Attributes["module_name"], + Provider: rs.Primary.Attributes["module_provider"], + Namespace: rs.Primary.Attributes["organization"], + RegistryName: "private", + } + + v, err := config.Client.TestVariables.Read(ctx, moduleID, rs.Primary.ID) + if err != nil { + return err + } + + *variable = *v + + return nil + } +} + +func testAccCheckTFETestVariableDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(ConfiguredClient) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "tfe_test_variable" { + continue + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No instance ID is set") + } + + moduleID := tfe.RegistryModuleID{ + Organization: rs.Primary.Attributes["organization"], + Name: rs.Primary.Attributes["module_name"], + Provider: rs.Primary.Attributes["module_provider"], + Namespace: rs.Primary.Attributes["organization"], + RegistryName: "private", + } + + _, err := config.Client.TestVariables.Read(ctx, moduleID, rs.Primary.ID) + if err == nil { + return fmt.Errorf("Variable %s still exists", rs.Primary.ID) + } + } + + return nil +} + +func testAccCheckTFETestVariableAttributesUpdate( + variable *tfe.Variable) resource.TestCheckFunc { + return func(s *terraform.State) error { + if variable.Key != "key_updated" { + return fmt.Errorf("Bad key: %s", variable.Key) + } + + if variable.Value != "" { + return fmt.Errorf("Bad value: %s", variable.Value) + } + + if variable.Description != "another description" { + return fmt.Errorf("Bad description: %s", variable.Description) + } + + if variable.Category != tfe.CategoryEnv { + return fmt.Errorf("Bad category: %s", variable.Category) + } + + if variable.HCL != true { + return fmt.Errorf("Bad HCL: %t", variable.HCL) + } + + if variable.Sensitive != true { + return fmt.Errorf("Bad sensitive: %t", variable.Sensitive) + } + + return nil + } +} + +func testAccTFETestVariable_test_variable(rInt int) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_oauth_client" "foobar" { + organization = tfe_organization.foobar.name + api_url = "https://api.github.com" + http_url = "https://github.com" + oauth_token = "%s" + service_provider = "github" +} + +resource "tfe_registry_module" "foobar" { + organization = tfe_organization.foobar.name + vcs_repo { + display_identifier = "%s" + identifier = "%s" + oauth_token_id = tfe_oauth_client.foobar.oauth_token_id + branch = "main" + tags = false +} + test_config { + tests_enabled = true + } +} + +resource "tfe_test_variable" "foobar" { + key = "key_test" + value = "value_test" + description = "some description" + category = "env" + organization = tfe_organization.foobar.name + module_name = tfe_registry_module.foobar.name + module_provider = tfe_registry_module.foobar.module_provider +} +`, + rInt, + envGithubToken, + envGithubRegistryModuleIdentifer, + envGithubRegistryModuleIdentifer) +} + +func testAccTFETestVariable_update(rInt int) string { + return fmt.Sprintf(` +resource "tfe_organization" "foobar" { + name = "tst-terraform-%d" + email = "admin@company.com" +} + +resource "tfe_oauth_client" "foobar" { + organization = tfe_organization.foobar.name + api_url = "https://api.github.com" + http_url = "https://github.com" + oauth_token = "%s" + service_provider = "github" +} + +resource "tfe_registry_module" "foobar" { + organization = tfe_organization.foobar.name + vcs_repo { + display_identifier = "%s" + identifier = "%s" + oauth_token_id = tfe_oauth_client.foobar.oauth_token_id + branch = "main" + tags = false + } + test_config { + tests_enabled = true + } +} + +resource "tfe_test_variable" "foobar" { + key = "key_updated" + value = "value_updated" + description = "another description" + category = "env" + hcl = true + sensitive = true + organization = tfe_organization.foobar.name + module_name = tfe_registry_module.foobar.name + module_provider = tfe_registry_module.foobar.module_provider +} +`, + rInt, + envGithubToken, + envGithubRegistryModuleIdentifer, + envGithubRegistryModuleIdentifer) +} diff --git a/website/docs/d/oauth_client.html.markdown b/website/docs/d/oauth_client.html.markdown index 0864c5bd6..3488a8a61 100644 --- a/website/docs/d/oauth_client.html.markdown +++ b/website/docs/d/oauth_client.html.markdown @@ -66,3 +66,5 @@ In addition to all arguments above, the following attributes are exported: * `organization` - The organization in which the OAuth client is registered. * `service_provider` - The API identifier of the OAuth service provider. * `service_provider_display_name` - The display name of the OAuth service provider. +* `organization_scoped` - Whether or not the agent pool can be used by all workspaces and projects in the organization. +* `project_ids` - IDs of the projects that use the oauth client. \ No newline at end of file diff --git a/website/docs/r/organization_default_settings.html.markdown b/website/docs/r/organization_default_settings.html.markdown index 72f4c3882..04bdee476 100644 --- a/website/docs/r/organization_default_settings.html.markdown +++ b/website/docs/r/organization_default_settings.html.markdown @@ -54,5 +54,5 @@ The following arguments are supported: Organization default execution mode can be imported; use `` as the import ID. For example: ```shell -terraform import tfe_organization_default_execution_mode.test my-org-name +terraform import tfe_organization_default_settings.test my-org-name ``` diff --git a/website/docs/r/tfe_test_variable.html.markdown b/website/docs/r/tfe_test_variable.html.markdown new file mode 100644 index 000000000..05a88c4e7 --- /dev/null +++ b/website/docs/r/tfe_test_variable.html.markdown @@ -0,0 +1,51 @@ +--- +layout: "tfe" +page_title: "Terraform Enterprise: tfe_test_variable" +description: |- + Manages environmet variables used for testing by modules in the Private Module Registry. +--- + +# tfe_test_variable + +Creates, updates and destroys environment variables used for testing in the Private Module Registry. + +## Example Usage + +```hcl +resource "tfe_organization" "test_org" { + name = "my-org-name" + email = "admin@company.com" +} + +resource "tfe_oauth_client" "test_client" { + organization = tfe_organization.test.name + api_url = "https://api.github.com" + http_url = "https://github.com" + oauth_token = "my-token-123" + service_provider = "github" +} + +resource "tfe_registry_module" "test_module" { + organization = "test-module" + vcs_repo { + display_identifier = "GH_NAME/REPO_NAME" + identifier = "GH_NAME/REPO_NAME" + oauth_token_id = tfe_oauth_client.test.oauth_token_id + branch = "main" + tags = false +} + test_config { + tests_enabled = true + } +} + +resource "tfe_test_variable" "tf_test_test_variable" { + key = "key_test" + value = "value_test" + description = "some description" + category = "env" + organization = tfe_organization.test.name + module_name = tfe_registry_module.test.name + module_provider = tfe_registry_module.test.module_provider +} +```