Skip to content

Commit

Permalink
Merge pull request #161 from zitadel/generate_machine_secret
Browse files Browse the repository at this point in the history
feat: generate machine secret
  • Loading branch information
eliobischof authored Feb 27, 2024
2 parents b6f449e + 348a7e7 commit 8bbd3a0
Show file tree
Hide file tree
Showing 10 changed files with 93 additions and 7 deletions.
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ terraform {
required_providers {
zitadel = {
source = "zitadel/zitadel"
version = "1.0.7"
version = "1.1.0"
}
}
}
Expand Down
8 changes: 6 additions & 2 deletions docs/resources/machine_user.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ resource "zitadel_machine_user" "default" {
user_name = "[email protected]"
name = "name"
description = "a machine user"
with_secret = false
}
```

Expand All @@ -33,9 +34,12 @@ resource "zitadel_machine_user" "default" {
- `access_token_type` (String) Access token type, supported values: ACCESS_TOKEN_TYPE_BEARER, ACCESS_TOKEN_TYPE_JWT
- `description` (String) Description of the user
- `org_id` (String) ID of the organization
- `with_secret` (Boolean) Generate machine secret, only applicable if creation or change from false

### Read-Only

- `client_id` (String, Sensitive) Value of the client ID if withSecret is true
- `client_secret` (String, Sensitive) Value of the client secret if withSecret is true
- `id` (String) The ID of this resource.
- `login_names` (List of String) Loginnames
- `preferred_login_name` (String) Preferred login name
Expand All @@ -44,6 +48,6 @@ resource "zitadel_machine_user" "default" {
## Import

```terraform
# The resource can be imported using the ID format `<id[:org_id]>`, e.g.
terraform import machine_user.imported '123456789012345678:123456789012345678'
# The resource can be imported using the ID format `<id:has_secret[:org_id][:client_id][:client_secret]>`, e.g.
terraform import machine_user.imported '123456789012345678:123456789012345678:true:my-machine-user:j76mh34CHVrGGoXPQOg80lch67FIxwc2qIXjBkZoB6oMbf31eGMkB6bvRyaPjR2t'
```
2 changes: 1 addition & 1 deletion examples/provider/provider.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ terraform {
required_providers {
zitadel = {
source = "zitadel/zitadel"
version = "1.0.7"
version = "1.1.0"
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions examples/provider/resources/machine_user-import.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# The resource can be imported using the ID format `<id[:org_id]>`, e.g.
terraform import machine_user.imported '123456789012345678:123456789012345678'
# The resource can be imported using the ID format `<id:has_secret[:org_id][:client_id][:client_secret]>`, e.g.
terraform import machine_user.imported '123456789012345678:123456789012345678:true:my-machine-user:j76mh34CHVrGGoXPQOg80lch67FIxwc2qIXjBkZoB6oMbf31eGMkB6bvRyaPjR2t'
1 change: 1 addition & 0 deletions examples/provider/resources/machine_user.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ resource "zitadel_machine_user" "default" {
user_name = "[email protected]"
name = "name"
description = "a machine user"
with_secret = false
}
5 changes: 5 additions & 0 deletions zitadel/helper/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"fmt"
"io"
"sort"
"strconv"
"strings"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
Expand Down Expand Up @@ -201,6 +202,10 @@ func ConvertNonEmpty(importValue string) (interface{}, error) {
return importValue, nil
}

func ConvertBool(importValue string) (interface{}, error) {
return strconv.ParseBool(importValue)
}

// ImportIDValidationError wraps err with a help message about the expected format if it is not nil
func ImportIDValidationError(givenID string, requiredKeys, optionalKeys []string, err error) error {
if err == nil {
Expand Down
3 changes: 3 additions & 0 deletions zitadel/machine_user/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ const (
nameVar = "name"
DescriptionVar = "description"
accessTokenTypeVar = "access_token_type"
WithSecretVar = "with_secret"
clientIDVar = "client_id"
clientSecretVar = "client_secret"
)

var (
Expand Down
46 changes: 46 additions & 0 deletions zitadel/machine_user/funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,22 @@ func create(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Dia
return diag.Errorf("failed to create machine user: %v", err)
}
d.SetId(respUser.UserId)

if d.Get(WithSecretVar).(bool) {
resp, err := client.GenerateMachineSecret(helper.CtxWithOrgID(ctx, d), &management.GenerateMachineSecretRequest{
UserId: respUser.UserId,
})
if err != nil {
return diag.Errorf("failed to generate machine user secret: %v", err)
}
if err := d.Set(clientIDVar, resp.GetClientId()); err != nil {
return diag.Errorf("failed to set %s of user: %v", clientIDVar, err)
}
if err := d.Set(clientSecretVar, resp.GetClientSecret()); err != nil {
return diag.Errorf("failed to set %s of user: %v", clientSecretVar, err)
}
}

// To avoid diffs for terraform plan -refresh=false right after creation, we query and set the computed values.
// The acceptance tests rely on this, too.
return read(ctx, d, m)
Expand Down Expand Up @@ -97,6 +113,36 @@ func update(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Dia
return diag.Errorf("failed to update machine user: %v", err)
}
}

if d.HasChange(WithSecretVar) {
if d.Get(WithSecretVar).(bool) {
resp, err := client.GenerateMachineSecret(helper.CtxWithOrgID(ctx, d), &management.GenerateMachineSecretRequest{
UserId: d.Id(),
})
if err != nil {
return diag.Errorf("failed to generate machine user secret: %v", err)
}
if err := d.Set(clientIDVar, resp.GetClientId()); err != nil {
return diag.Errorf("failed to set %s of user: %v", clientIDVar, err)
}
if err := d.Set(clientSecretVar, resp.GetClientSecret()); err != nil {
return diag.Errorf("failed to set %s of user: %v", clientSecretVar, err)
}
} else {
_, err := client.RemoveMachineSecret(helper.CtxWithOrgID(ctx, d), &management.RemoveMachineSecretRequest{
UserId: d.Id(),
})
if err != nil {
return diag.Errorf("failed to remove machine user secret: %v", err)
}
if err := d.Set(clientIDVar, ""); err != nil {
return diag.Errorf("failed to set %s of user: %v", clientIDVar, err)
}
if err := d.Set(clientSecretVar, ""); err != nil {
return diag.Errorf("failed to set %s of user: %v", clientSecretVar, err)
}
}
}
return nil
}

Expand Down
25 changes: 24 additions & 1 deletion zitadel/machine_user/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,34 @@ func GetResource() *schema.Resource {
},
Default: defaultAccessTokenType,
},
WithSecretVar: {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "Generate machine secret, only applicable if creation or change from false",
},
clientIDVar: {
Type: schema.TypeString,
Computed: true,
Description: "Value of the client ID if withSecret is true",
Sensitive: true,
},
clientSecretVar: {
Type: schema.TypeString,
Computed: true,
Description: "Value of the client secret if withSecret is true",
Sensitive: true,
},
},
ReadContext: read,
CreateContext: create,
DeleteContext: delete,
UpdateContext: update,
Importer: helper.ImportWithIDAndOptionalOrg(UserIDVar),
Importer: helper.ImportWithIDAndOptionalOrg(
UserIDVar,
helper.NewImportAttribute(WithSecretVar, helper.ConvertBool, false),
helper.NewImportAttribute(clientIDVar, helper.ConvertNonEmpty, true),
helper.NewImportAttribute(clientSecretVar, helper.ConvertNonEmpty, true),
),
}
}
4 changes: 4 additions & 0 deletions zitadel/machine_user/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package machine_user_test

import (
"fmt"
"strconv"
"strings"
"testing"

Expand Down Expand Up @@ -33,6 +34,9 @@ func TestAccMachineUser(t *testing.T) {
test_utils.CheckIsNotFoundFromPropertyCheck(checkRemoteProperty(frame), ""),
test_utils.ChainImportStateIdFuncs(
test_utils.ImportResourceId(frame.BaseTestFrame),
func(state *terraform.State) (string, error) {
return strconv.FormatBool(test_utils.AttributeValue(t, machine_user.WithSecretVar, exampleAttributes).True()), nil
},
test_utils.ImportOrgId(frame),
),
)
Expand Down

0 comments on commit 8bbd3a0

Please sign in to comment.