Skip to content

Commit 8266a14

Browse files
authored
Merge pull request #292 from port-labs/ft/portOrg
feat: add organization secret management with terraform provider
2 parents 8082b85 + 83c3fb8 commit 8266a14

File tree

12 files changed

+628
-0
lines changed

12 files changed

+628
-0
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "port_organization_secret Resource - terraform-provider-port-labs"
4+
subcategory: ""
5+
description: |-
6+
Organization secret resource
7+
---
8+
9+
# port_organization_secret (Resource)
10+
11+
Organization secret resource
12+
13+
14+
15+
<!-- schema generated by tfplugindocs -->
16+
## Schema
17+
18+
### Required
19+
20+
- `secret_name` (String) The name of the organization secret
21+
- `secret_value` (String, Sensitive) The value of the organization secret
22+
23+
### Optional
24+
25+
- `description` (String) The description of the organization secret
26+
27+
### Read-Only
28+
29+
- `id` (String) The ID of this resource.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Example with sensitive variable
2+
variable "api_secret_name" {
3+
type = string
4+
default = "api_key"
5+
}
6+
7+
variable "home_secret_name" {
8+
type = string
9+
default = "home_url"
10+
}
11+
variable "guest_secret_name" {
12+
type = string
13+
default = "guest_url"
14+
}
15+
variable "api_key" {
16+
type = string
17+
sensitive = true
18+
default = "api_key"
19+
}
20+
variable "api_description" {
21+
type = string
22+
default = "api description"
23+
}
24+
25+
variable "home_url" {
26+
type = string
27+
default = "home url"
28+
}
29+
30+
variable "guest_url" {
31+
type = string
32+
default = "guest url"
33+
}
34+
35+
variable "home_description" {
36+
type = string
37+
default = "home description"
38+
}
39+
40+
variable "guest_description" {
41+
type = string
42+
default = "guest description"
43+
}
44+
45+
resource "port_organization_secret" "api_key" {
46+
secret_name = var.api_secret_name
47+
secret_value = var.api_key
48+
description = var.api_description
49+
}
50+
51+
resource "port_organization_secret" "home_url" {
52+
secret_name = var.home_secret_name
53+
secret_value = var.home_url
54+
description = var.home_description
55+
}
56+
57+
resource "port_organization_secret" "guest_url" {
58+
secret_name = var.guest_secret_name
59+
secret_value = var.guest_url
60+
description = var.guest_description
61+
}
62+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../provider.tf

internal/cli/models.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,3 +678,14 @@ type Organization struct {
678678
Name string `json:"name"`
679679
FeatureFlags []string `json:"featureFlags"`
680680
}
681+
682+
type OrganizationSecret struct {
683+
SecretName string `json:"secretName,omitempty"`
684+
SecretValue *string `json:"secretValue,omitempty"`
685+
Description *string `json:"description,omitempty"`
686+
}
687+
688+
type OrganizationSecretBody struct {
689+
OK bool `json:"ok"`
690+
Secret OrganizationSecret `json:"secret"`
691+
}

internal/cli/organization.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ package cli
22

33
import (
44
"context"
5+
"encoding/json"
56
"fmt"
67
)
78

89
const orgUrl = "/v1/organization"
10+
const orgSecretsUrl = "/v1/organization/secrets"
911

1012
func (c *PortClient) ReadOrganization(ctx context.Context) (*Organization, int, error) {
1113
pb := &PortBody{}
@@ -24,3 +26,88 @@ func (c *PortClient) ReadOrganization(ctx context.Context) (*Organization, int,
2426
}
2527
return pb.Organization, resp.StatusCode(), nil
2628
}
29+
30+
func (c *PortClient) CreateOrganizationSecret(ctx context.Context, secret *OrganizationSecret) (*OrganizationSecret, error) {
31+
resp, err := c.Client.R().
32+
SetBody(secret).
33+
SetContext(ctx).
34+
Post(orgSecretsUrl)
35+
36+
if err != nil {
37+
return nil, err
38+
}
39+
var pb OrganizationSecretBody
40+
err = json.Unmarshal(resp.Body(), &pb)
41+
if err != nil {
42+
return nil, err
43+
}
44+
if !pb.OK {
45+
return nil, fmt.Errorf("failed to create organization secret, got: %s", resp.Body())
46+
}
47+
48+
return &pb.Secret, nil
49+
}
50+
51+
func (c *PortClient) ReadOrganizationSecret(ctx context.Context, secretName string) (*OrganizationSecret, int, error) {
52+
var pb OrganizationSecretBody
53+
resp, err := c.Client.R().
54+
SetContext(ctx).
55+
SetHeader("Accept", "application/json").
56+
SetPathParam("secretName", secretName).
57+
SetResult(&pb).
58+
Get(orgSecretsUrl + "/{secretName}")
59+
if err != nil {
60+
return nil, 0, err
61+
} else if resp.IsError() {
62+
return nil, resp.StatusCode(), fmt.Errorf("failed to read organization secret, got: %s", resp.Body())
63+
}
64+
if !pb.OK {
65+
return nil, resp.StatusCode(), fmt.Errorf("failed to read organization secret, got: %s", resp.Body())
66+
}
67+
68+
return &pb.Secret, resp.StatusCode(), nil
69+
}
70+
71+
func (c *PortClient) UpdateOrganizationSecret(ctx context.Context, secretName string, secret *OrganizationSecret) (*OrganizationSecret, error) {
72+
resp, err := c.Client.R().
73+
SetBody(secret).
74+
SetContext(ctx).
75+
SetPathParam("secretName", secretName).
76+
Patch(orgSecretsUrl + "/{secretName}")
77+
78+
if err != nil {
79+
return nil, err
80+
}
81+
var pb OrganizationSecretBody
82+
err = json.Unmarshal(resp.Body(), &pb)
83+
if err != nil {
84+
return nil, err
85+
}
86+
if !pb.OK {
87+
return nil, fmt.Errorf("failed to update organization secret, got: %s", resp.Body())
88+
}
89+
90+
return &pb.Secret, nil
91+
}
92+
93+
func (c *PortClient) DeleteOrganizationSecret(ctx context.Context, secretName string) error {
94+
resp, err := c.Client.R().
95+
SetContext(ctx).
96+
SetHeader("Accept", "application/json").
97+
SetPathParam("secretName", secretName).
98+
Delete(orgSecretsUrl + "/{secretName}")
99+
100+
if err != nil {
101+
return err
102+
}
103+
var pb PortBodyDelete
104+
err = json.Unmarshal(resp.Body(), &pb)
105+
if err != nil {
106+
return err
107+
}
108+
109+
if !pb.Ok {
110+
return fmt.Errorf("failed to delete organization secret, got: %s", string(resp.Body()))
111+
}
112+
return nil
113+
}

port/organization/model.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package organization
2+
3+
import (
4+
"github.com/hashicorp/terraform-plugin-framework/types"
5+
)
6+
7+
type OrganizationSecretModel struct {
8+
ID types.String `tfsdk:"id"`
9+
SecretName types.String `tfsdk:"secret_name"`
10+
SecretValue types.String `tfsdk:"secret_value"`
11+
Description types.String `tfsdk:"description"`
12+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package organization
2+
3+
import (
4+
"context"
5+
6+
"github.com/port-labs/terraform-provider-port-labs/v2/internal/cli"
7+
)
8+
9+
func organizationSecretResourceToPortBody(ctx context.Context, state *OrganizationSecretModel) (*cli.OrganizationSecret, error) {
10+
secret := &cli.OrganizationSecret{
11+
SecretName: state.SecretName.ValueString(),
12+
}
13+
14+
if !state.SecretValue.IsNull() {
15+
secretValue := state.SecretValue.ValueString()
16+
secret.SecretValue = &secretValue
17+
}
18+
19+
if !state.Description.IsNull() {
20+
description := state.Description.ValueString()
21+
secret.Description = &description
22+
}
23+
24+
return secret, nil
25+
}
26+
27+
func organizationSecretResourceToPortBodyForUpdate(ctx context.Context, state *OrganizationSecretModel) (*cli.OrganizationSecret, error) {
28+
secret := &cli.OrganizationSecret{}
29+
30+
if !state.SecretValue.IsNull() {
31+
secretValue := state.SecretValue.ValueString()
32+
secret.SecretValue = &secretValue
33+
}
34+
35+
if !state.Description.IsNull() {
36+
description := state.Description.ValueString()
37+
secret.Description = &description
38+
} else {
39+
emptyDesc := ""
40+
secret.Description = &emptyDesc
41+
}
42+
43+
return secret, nil
44+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package organization
2+
3+
import (
4+
"context"
5+
6+
"github.com/hashicorp/terraform-plugin-framework/types"
7+
"github.com/port-labs/terraform-provider-port-labs/v2/internal/cli"
8+
)
9+
10+
func refreshOrganizationSecretState(ctx context.Context, state *OrganizationSecretModel, secret *cli.OrganizationSecret) error {
11+
state.ID = types.StringValue(secret.SecretName)
12+
state.SecretName = types.StringValue(secret.SecretName)
13+
14+
if secret.Description != nil && *secret.Description != "" {
15+
state.Description = types.StringValue(*secret.Description)
16+
} else {
17+
state.Description = types.StringNull()
18+
}
19+
20+
// Note: SecretValue is not returned by the API for security reasons
21+
// We keep the existing state value
22+
return nil
23+
}

0 commit comments

Comments
 (0)