From 64b6126b409cb53b00b73f43c557ebdfca5e88e2 Mon Sep 17 00:00:00 2001 From: Frank Celler Date: Thu, 17 Mar 2022 14:45:16 +0100 Subject: [PATCH] fixed email change --- .github/workflows/release.yml | 15 ++--- .goreleaser.yml | 56 +++++++++--------- README.md | 70 +++++++++++++++-------- client/client_test.go | 2 +- zoom/readme.md | 3 +- zoom/resource_user.go | 104 +++++++++++++++++----------------- 6 files changed, 134 insertions(+), 116 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e7d4f49..f7bb4bc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,27 +18,22 @@ jobs: goreleaser: runs-on: ubuntu-latest steps: - - - name: Checkout + - name: Checkout uses: actions/checkout@v2 - - - name: Unshallow + - name: Unshallow run: git fetch --prune --unshallow - - - name: Set up Go + - name: Set up Go uses: actions/setup-go@v2 with: go-version: 1.16 - - - name: Import GPG key + - name: Import GPG key id: import_gpg uses: hashicorp/ghaction-import-gpg@v2.1.0 env: # These secrets will need to be configured for the repository: GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} PASSPHRASE: ${{ secrets.PASSPHRASE }} - - - name: Run GoReleaser + - name: Run GoReleaser uses: goreleaser/goreleaser-action@v2 with: version: latest diff --git a/.goreleaser.yml b/.goreleaser.yml index 77d6355..3dc7809 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -5,33 +5,33 @@ before: # this is just an example and not a requirement for provider building/publishing - go mod tidy builds: -- env: - # goreleaser does not work with CGO, it could also complicate - # usage by users in CI/CD systems like Terraform Cloud where - # they are unable to install libraries. - - CGO_ENABLED=0 - mod_timestamp: '{{ .CommitTimestamp }}' - flags: - - -trimpath - ldflags: - - '-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}' - goos: - - freebsd - - windows - - linux - - darwin - goarch: - - amd64 - - '386' - - arm - - arm64 - ignore: - - goos: darwin - goarch: '386' - binary: '{{ .ProjectName }}_v{{ .Version }}' + - env: + # goreleaser does not work with CGO, it could also complicate + # usage by users in CI/CD systems like Terraform Cloud where + # they are unable to install libraries. + - CGO_ENABLED=0 + mod_timestamp: '{{ .CommitTimestamp }}' + flags: + - -trimpath + ldflags: + - '-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}' + goos: + - freebsd + - windows + - linux + - darwin + goarch: + - amd64 + - '386' + - arm + - arm64 + ignore: + - goos: darwin + goarch: '386' + binary: '{{ .ProjectName }}_v{{ .Version }}' archives: -- format: zip - name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}' + - format: zip + name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}' checksum: name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS' algorithm: sha256 @@ -48,7 +48,7 @@ signs: - "--detach-sign" - "${artifact}" release: - # If you want to manually examine the release before its live, uncomment this line: - # draft: true +# If you want to manually examine the release before its live, uncomment this line: +# draft: true changelog: skip: true diff --git a/README.md b/README.md index 057b8ea..2127afe 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ -This terraform provider allows to perform Create ,Read ,Update, Delete, Import and Deactivate Zoom Users. +This terraform provider allows to perform Create ,Read ,Update, Delete, Import and Deactivate Zoom Users. ## Requirements * [Go](https://golang.org/doc/install) 1.16
* [Terraform](https://www.terraform.io/downloads.html) 0.13.x
-* [Zoom](https://zoom.us/) Pro/Premium account +* [Zoom](https://zoom.us/) Pro/Premium account * [Zoom API Documentations](https://marketplace.zoom.us/docs/api-reference/zoom-api/users) - ## Application Account + ***This provider can only be successfully tested on a premium paid zoom account.***

### Setup @@ -16,42 +16,52 @@ This terraform provider allows to perform Create ,Read ,Update, Delete, Import a 1. Create a zoom account with paid subscription (PRO Plan/Business Account). (https://zoom.us/)
### API Authentication + 1. Go to [Zoom Marketplace](https://marketplace.zoom.us/)
2. Click on `Build App`. For our purpose we need to make a JWT App.
3. Follow this [Create JWT Zoom App](https://marketplace.zoom.us/docs/guides/build/jwt-app) website to make an app.
-4. This app will provide us with the zoom_api_secret, zoom_api_key, and ZOOM_TOKEN which will be needed to configure our provider and make request.
- +4. This app will provide us with the zoom_api_secret, zoom_api_key, and ZOOM_TOKEN which will be needed to configure our + provider and make request.
## Building the Provider -1. Clone the repository, add all the dependencies and create a vendor directory that contains all dependencies. For this, run the following commands: + +1. Clone the repository, add all the dependencies and create a vendor directory that contains all dependencies. For + this, run the following commands: + ```cd terraform-provider-zoom go mod init terraform-provider-zoom go mod tidy go mod vendor ``` - ## Managing terraform plugins -1. Run the following command to create a vendor subdirectory which will comprise of all provider dependencies.
+ +1. Run the following command to create a vendor subdirectory which will comprise of all provider dependencies.
+ ``` %APPDATA%/terraform.d/plugins/${host_name}/${namespace}/${type}/${version}/${target} ``` -Command: + +Command: + ```bash mkdir -p %APPDATA%/terraform.d/plugins/hashicorp.com/edu/zoom/0.2.0/[OS_ARCH] ``` + For eg. `mkdir -p %APPDATA%/terraform.d/plugins/hashicorp.com/edu/zoom/0.2.0/windows_amd64`
-2. Run `go build -o terraform-provider-zoom.exe`. This will save the binary (`.exe`) file in the main/root directory.
+2. Run `go build -o terraform-provider-zoom.exe`. This will save the binary (`.exe`) file in the main/root + directory.
3. Run this command to move this binary file to appropriate location. + ``` move terraform-provider-zoom.exe %APPDATA%\terraform.d\plugins\hashicorp.com\edu\zoom\0.2.0\[OS_ARCH] ``` + [OR] 1. Manually move the file from current directory to destination directory
- ## Working with terraform ### Application Credential Integration in terraform @@ -61,35 +71,43 @@ For eg. `mkdir -p %APPDATA%/terraform.d/plugins/hashicorp.com/edu/zoom/0.2.0/win 3. Assign the above credentials to the respective field in the `provider` block. ### Basic Terraform Commands + 1. `terraform init` - To initialize a working directory containing Terraform configuration files. 2. `terraform plan` - To create an execution plan. Displays the changes to be done. 3. `terraform apply` - To execute the actions proposed in a Terraform plan. Apply the changes. ### Create User -1. Add the user email, first name, last name, status, license_type, deartment, job_title, location in the respective field as shown in [example usage](#example-usage). + +1. Add the user email, first name, last name, status, license_type, deartment, job_title, location in the respective + field as shown in [example usage](#example-usage). 2. Run the basic terraform commands.
3. On successful execution, sends an account setup mail to user.
### Update the user -1. Update the data of the user in the `resource` block as show in [example usage](#example-usage) and run the basic terraform commands to update user. - User is not allowed to update `email`. - + +1. Update the data of the user in the `resource` block as show in [example usage](#example-usage) and run the basic + terraform commands to update user. User is not allowed to update `email`. + 2. Update the `status` of User from `active` to `inactive` or viceversa and run the basic terraform commands. ### Read the User Data + Add `data` and `output` blocks as shown in the [example usage](#example-usage) and run the basic terraform commands. ### Delete the user + Delete the `resource` block of the user and run `terraform apply`. #### Import a User Data -1. Write manually a `resource` configuration block for the user as shown in [example usage](#example-usage). Imported user will be mapped to this block. -2. Run the command `terraform import zoom_user.user1 [EMAIL_ID]` to import user. -3. Run `terraform plan`, if output shows `0 to add, 0 to change and 0 to destroy` user import is successful, otherwise recheck the user data in `resource` block with user data in Zoom website. - +1. Write manually a `resource` configuration block for the user as shown in [example usage](#example-usage). Imported + user will be mapped to this block. +2. Run the command `terraform import zoom_user.user1 [EMAIL_ID]` to import user. +3. Run `terraform plan`, if output shows `0 to add, 0 to change and 0 to destroy` user import is successful, otherwise + recheck the user data in `resource` block with user data in Zoom website. ## Example Usage + ```terraform terraform { required_providers { @@ -128,9 +146,12 @@ output "user1" { ## Argument Reference -* `zoom_api_key`(Required, string) - The Zoom API Key. This may also be set via the `"ZOOM_API_KEY"` environment variable. -* `zoom_api_secret`(Required, string) - The Zoom API Secret. This may also be set via the `"ZOOM_API_SECRET"` environment variable. -* `timeout_minutes` (Optional, int) - The duration for which retries to be performed when an API request fails with API Rate limit error. This may also be set via the `"ZOOM_TIMEOUT_MINUTES"` environment variable. Default value is 2. +* `zoom_api_key`(Required, string) - The Zoom API Key. This may also be set via the `"ZOOM_API_KEY"` environment + variable. +* `zoom_api_secret`(Required, string) - The Zoom API Secret. This may also be set via the `"ZOOM_API_SECRET"` + environment variable. +* `timeout_minutes` (Optional, int) - The duration for which retries to be performed when an API request fails with + API Rate limit error. This may also be set via the `"ZOOM_TIMEOUT_MINUTES"` environment variable. Default value is 2. * `email`(Required, string) - The email id associated with the user account. * `first_name`(Required, string) - First name of the User. * `last_name`(Required, string) - Last Name / Family Name / Surname of the User. @@ -142,14 +163,15 @@ output "user1" { * `location`(Optional, string) - Department of the particular user. * `pmi`(Optional, integer) - Personal Meeting ID of the user. * `use_pmi`(Optional, bool) - If true means use PMI for instant meetings. -* `timezone`(Optional, string) - Time zone ID of user. For values refer [here](https://marketplace.zoom.us/docs/api-reference/other-references/abbreviation-lists#timezones). +* `timezone`(Optional, string) - Time zone ID of user. For values + refer [here](https://marketplace.zoom.us/docs/api-reference/other-references/abbreviation-lists#timezones). * `vanity_name`(Optional, string) - Personal Meeting Room Name. * `host_key`(Optional, string) - Host Key of user. * `group_id`(Optional, string) - Unique identifier of group to which user to be added. * `manager`(Optional, string) - Name or Email of Manager of the User. * `phone_numbers`(Optional, set of maps) - Maps contain keys code, country, number, label. * `language`(Optional, string) - Language of User. -* `cms_user_id`(Optional, string) - CMS User ID of User. +* `cms_user_id`(Optional, string) - CMS User ID of User. * `pronouns`(Optional, string) - User's pronouns. * `pronouns_option`(Optional, int) - User's display pronouns setting. * `role_name`(Computed, string) - Current role of the user i.e., (Admin,Member). diff --git a/client/client_test.go b/client/client_test.go index e578148..0191444 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -97,7 +97,7 @@ func TestClient_NewItem(t *testing.T) { t.Run(tc.testName, func(t *testing.T) { apiToken := token.GenerateToken(os.Getenv("ZOOM_API_SECRET"), os.Getenv(os.Getenv("ZOOM_API_KEY"))) client := NewClient(apiToken, 2) - err := client.NewUser(tc.user) + _, err := client.NewUser(tc.user) if tc.expectErr { assert.Error(t, err) return diff --git a/zoom/readme.md b/zoom/readme.md index bd2af54..0d21141 100644 --- a/zoom/readme.md +++ b/zoom/readme.md @@ -14,7 +14,8 @@ 1-make TF_ACC = true (set environment variable,this is to run the acceptance testing)
-2-Hashicorp has provider some inbuilt packages which we can use to implemet our testing ie. resource("github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource")
+2-Hashicorp has provider some inbuilt packages which we can use to implemet our testing ie. resource(" +github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource")
3-we set up a resource.Test and provide it with the following:
PreCheck,Providers,CheckDestroy,Steps
diff --git a/zoom/resource_user.go b/zoom/resource_user.go index 384a894..5e9980a 100644 --- a/zoom/resource_user.go +++ b/zoom/resource_user.go @@ -32,115 +32,115 @@ func resourceUser() *schema.Resource { StateContext: resourceUserImporter, }, Schema: map[string]*schema.Schema{ - "id": &schema.Schema{ + "id": { Type: schema.TypeString, Optional: true, Computed: true, }, - "email": &schema.Schema{ + "email": { Type: schema.TypeString, Required: true, ValidateFunc: validateEmail, }, - "first_name": &schema.Schema{ + "first_name": { Type: schema.TypeString, Required: true, }, - "last_name": &schema.Schema{ + "last_name": { Type: schema.TypeString, Required: true, }, - "status": &schema.Schema{ + "status": { Type: schema.TypeString, Optional: true, Computed: true, }, - "license_type": &schema.Schema{ + "license_type": { Type: schema.TypeInt, Required: true, }, - "pmi": &schema.Schema{ + "pmi": { Type: schema.TypeInt, Optional: true, Computed: true, }, - "use_pmi": &schema.Schema{ + "use_pmi": { Type: schema.TypeBool, Optional: true, Computed: true, }, - "timezone": &schema.Schema{ + "timezone": { Type: schema.TypeString, Optional: true, Computed: true, }, - "language": &schema.Schema{ + "language": { Type: schema.TypeString, Optional: true, Computed: true, }, - "vanity_name": &schema.Schema{ + "vanity_name": { Type: schema.TypeString, Optional: true, Computed: true, }, - "host_key": &schema.Schema{ + "host_key": { Type: schema.TypeString, Optional: true, Computed: true, }, - "cms_user_id": &schema.Schema{ + "cms_user_id": { Type: schema.TypeString, Optional: true, Computed: true, }, - "company": &schema.Schema{ + "company": { Type: schema.TypeString, Optional: true, Computed: true, }, - "group_id": &schema.Schema{ + "group_id": { Type: schema.TypeString, Optional: true, Computed: true, }, - "manager": &schema.Schema{ + "manager": { Type: schema.TypeString, Optional: true, Computed: true, }, - "pronouns": &schema.Schema{ + "pronouns": { Type: schema.TypeString, Optional: true, Computed: true, }, - "pronouns_option": &schema.Schema{ + "pronouns_option": { Type: schema.TypeInt, Optional: true, Computed: true, }, - "phone_numbers": &schema.Schema{ + "phone_numbers": { Type: schema.TypeSet, Optional: true, Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "country": &schema.Schema{ + "country": { Type: schema.TypeString, Optional: true, Computed: true, }, - "code": &schema.Schema{ + "code": { Type: schema.TypeString, Optional: true, Computed: true, }, - "number": &schema.Schema{ + "number": { Type: schema.TypeString, Optional: true, Computed: true, }, - "label": &schema.Schema{ + "label": { Type: schema.TypeString, Optional: true, Computed: true, @@ -148,22 +148,22 @@ func resourceUser() *schema.Resource { }, }, }, - "role_name": &schema.Schema{ + "role_name": { Type: schema.TypeString, Optional: true, Computed: true, }, - "department": &schema.Schema{ + "department": { Type: schema.TypeString, Optional: true, Computed: true, }, - "job_title": &schema.Schema{ + "job_title": { Type: schema.TypeString, Optional: true, Computed: true, }, - "location": &schema.Schema{ + "location": { Type: schema.TypeString, Optional: true, Computed: true, @@ -206,8 +206,8 @@ func resourceUserCreate(ctx context.Context, d *schema.ResourceData, m interface return diag.FromErr(retryErr) } d.SetId(id) - d.Set("email", user.Email) resourceUserRead(ctx, d, m) + d.Set("email", user.Email) return diags } @@ -272,7 +272,11 @@ func resourceUserUpdate(ctx context.Context, d *schema.ResourceData, m interface var _ diag.Diagnostics apiClient := m.(*client.Client) var diags diag.Diagnostics - if d.HasChange("email") { + + oldI, statusI := d.GetChange("status") + isPending := oldI.(string) == "pending" + + if !isPending && d.HasChange("email") { if v, ok := d.GetOk("email"); ok { newEmail := v.(string) retryErr := resource.Retry(time.Duration(apiClient.TimeoutMinutes)*time.Minute, func() *resource.RetryError { @@ -291,31 +295,27 @@ func resourceUserUpdate(ctx context.Context, d *schema.ResourceData, m interface d.SetId(newEmail) } } - if d.HasChange("status") { - if v, ok := d.GetOk("status"); ok { - old, _ := d.GetChange("status") - oldStatus := old.(string) - status := v.(string) - if oldStatus != "pending" || status == "inactive" { - var action string - if status == "active" { - action = "activate" - } else if status == "inactive" { - action = "deactivate" - } - retryErr := resource.Retry(time.Duration(apiClient.TimeoutMinutes)*time.Minute, func() *resource.RetryError { - if err := apiClient.ChangeUserStatus(d.Id(), action); err != nil { - if apiClient.IsRetry(err) { - return resource.RetryableError(err) - } - return resource.NonRetryableError(err) + if isPending || d.HasChange("status") { + status := statusI.(string) + if !isPending || status == "inactive" { + var action string + if status == "active" { + action = "activate" + } else if status == "inactive" { + action = "deactivate" + } + retryErr := resource.Retry(time.Duration(apiClient.TimeoutMinutes)*time.Minute, func() *resource.RetryError { + if err := apiClient.ChangeUserStatus(d.Id(), action); err != nil { + if apiClient.IsRetry(err) { + return resource.RetryableError(err) } - return nil - }) - if retryErr != nil { - time.Sleep(2 * time.Second) - return diag.FromErr(retryErr) + return resource.NonRetryableError(err) } + return nil + }) + if retryErr != nil { + time.Sleep(2 * time.Second) + return diag.FromErr(retryErr) } } } else {