Skip to content

Commit 37e7d7b

Browse files
committed
Add RegistryAccount resource type
Although registry accounts are just service accounts, they have distinctly different behaviour to other service accounts: user/pass rather than keys, and inability to rotate credentials. Rather than trying to make the Service Account resource type handle both cases, and have to have the user do conditional behaviour based on what type of service account it is, I think it makes sense to just make a new type of resource.
1 parent 3b2d997 commit 37e7d7b

File tree

4 files changed

+258
-0
lines changed

4 files changed

+258
-0
lines changed

docs/resources/registry_account.md

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: "tlspc_registry_account Resource - tlspc"
4+
subcategory: ""
5+
description: |-
6+
7+
---
8+
9+
# tlspc_registry_account (Resource)
10+
11+
12+
13+
14+
15+
<!-- schema generated by tfplugindocs -->
16+
## Schema
17+
18+
### Required
19+
20+
- `credential_lifetime` (Number)
21+
- `name` (String)
22+
- `owner` (String)
23+
- `scopes` (Set of String)
24+
25+
### Read-Only
26+
27+
- `id` (String) The ID of this resource.
28+
- `oci_account_name` (String)
29+
- `oci_registry_token` (String, Sensitive)

internal/provider/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ func (p *tlspcProvider) Resources(ctx context.Context) []func() resource.Resourc
8484
return []func() resource.Resource{
8585
NewTeamResource,
8686
NewServiceAccountResource,
87+
NewRegistryAccountResource,
8788
}
8889
}
8990

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
// Copyright (c) Venafi, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package provider
5+
6+
import (
7+
"context"
8+
"fmt"
9+
10+
"terraform-provider-tlspc/internal/tlspc"
11+
12+
"github.com/hashicorp/terraform-plugin-framework/path"
13+
"github.com/hashicorp/terraform-plugin-framework/resource"
14+
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
15+
"github.com/hashicorp/terraform-plugin-framework/types"
16+
)
17+
18+
var (
19+
_ resource.Resource = &registryAccountResource{}
20+
_ resource.ResourceWithConfigure = &registryAccountResource{}
21+
_ resource.ResourceWithImportState = &registryAccountResource{}
22+
)
23+
24+
type registryAccountResource struct {
25+
client *tlspc.Client
26+
}
27+
28+
func NewRegistryAccountResource() resource.Resource {
29+
return &registryAccountResource{}
30+
}
31+
32+
func (r *registryAccountResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
33+
resp.TypeName = req.ProviderTypeName + "_registry_account"
34+
}
35+
36+
func (r *registryAccountResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
37+
resp.Schema = schema.Schema{
38+
Attributes: map[string]schema.Attribute{
39+
"id": schema.StringAttribute{
40+
Computed: true,
41+
},
42+
"name": schema.StringAttribute{
43+
Required: true,
44+
},
45+
"owner": schema.StringAttribute{
46+
Required: true,
47+
},
48+
"scopes": schema.SetAttribute{
49+
Required: true,
50+
ElementType: types.StringType,
51+
},
52+
"oci_account_name": schema.StringAttribute{
53+
Computed: true,
54+
},
55+
"oci_registry_token": schema.StringAttribute{
56+
Computed: true,
57+
Sensitive: true,
58+
},
59+
"credential_lifetime": schema.Int32Attribute{
60+
Required: true,
61+
},
62+
},
63+
}
64+
}
65+
66+
func (r *registryAccountResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
67+
if req.ProviderData == nil {
68+
return
69+
}
70+
71+
client, ok := req.ProviderData.(*tlspc.Client)
72+
73+
if !ok {
74+
resp.Diagnostics.AddError(
75+
"Unexpected Data Source Configure Type",
76+
fmt.Sprintf("Expected *tlspc.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
77+
)
78+
79+
return
80+
}
81+
82+
r.client = client
83+
}
84+
85+
type registryAccountResourceModel struct {
86+
ID types.String `tfsdk:"id"`
87+
Name types.String `tfsdk:"name"`
88+
Owner types.String `tfsdk:"owner"`
89+
Scopes []types.String `tfsdk:"scopes"`
90+
OciAccountName types.String `tfsdk:"oci_account_name"`
91+
OciRegistryToken types.String `tfsdk:"oci_registry_token"`
92+
CredentialLifetime types.Int32 `tfsdk:"credential_lifetime"`
93+
}
94+
95+
func (r *registryAccountResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
96+
var plan registryAccountResourceModel
97+
diags := req.Plan.Get(ctx, &plan)
98+
resp.Diagnostics.Append(diags...)
99+
if resp.Diagnostics.HasError() {
100+
return
101+
}
102+
scopes := []string{}
103+
for _, v := range plan.Scopes {
104+
scopes = append(scopes, v.ValueString())
105+
}
106+
107+
registryAccount := tlspc.ServiceAccount{
108+
Name: plan.Name.ValueString(),
109+
Owner: plan.Owner.ValueString(),
110+
Scopes: scopes,
111+
CredentialLifetime: plan.CredentialLifetime.ValueInt32(),
112+
AuthenticationType: "ociToken",
113+
}
114+
115+
created, err := r.client.CreateServiceAccount(registryAccount)
116+
if err != nil {
117+
resp.Diagnostics.AddError(
118+
"Error creating registryAccount",
119+
"Could not create registryAccount, unexpected error: "+err.Error(),
120+
)
121+
return
122+
}
123+
plan.ID = types.StringValue(created.ID)
124+
plan.OciAccountName = types.StringValue(created.OciAccountName)
125+
plan.OciRegistryToken = types.StringValue(created.OciRegistryToken)
126+
diags = resp.State.Set(ctx, plan)
127+
resp.Diagnostics.Append(diags...)
128+
}
129+
130+
func (r *registryAccountResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
131+
var state registryAccountResourceModel
132+
133+
diags := req.State.Get(ctx, &state)
134+
resp.Diagnostics.Append(diags...)
135+
if resp.Diagnostics.HasError() {
136+
return
137+
}
138+
139+
sa, err := r.client.GetServiceAccount(state.ID.ValueString())
140+
if err != nil {
141+
resp.Diagnostics.AddError(
142+
"Error Reading Registry Account",
143+
"Could not read registryaccount ID "+state.ID.ValueString()+": "+err.Error(),
144+
)
145+
return
146+
}
147+
148+
state.ID = types.StringValue(sa.ID)
149+
state.Name = types.StringValue(sa.Name)
150+
state.Owner = types.StringValue(sa.Owner)
151+
152+
scopes := []types.String{}
153+
for _, v := range sa.Scopes {
154+
scopes = append(scopes, types.StringValue(v))
155+
}
156+
state.Scopes = scopes
157+
158+
diags = resp.State.Set(ctx, state)
159+
resp.Diagnostics.Append(diags...)
160+
}
161+
162+
func (r *registryAccountResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
163+
var plan, state registryAccountResourceModel
164+
165+
diags := req.State.Get(ctx, &state)
166+
resp.Diagnostics.Append(diags...)
167+
if resp.Diagnostics.HasError() {
168+
return
169+
}
170+
diags = req.Plan.Get(ctx, &plan)
171+
resp.Diagnostics.Append(diags...)
172+
if resp.Diagnostics.HasError() {
173+
return
174+
}
175+
scopes := []string{}
176+
for _, v := range plan.Scopes {
177+
scopes = append(scopes, v.ValueString())
178+
}
179+
180+
registryAccount := tlspc.ServiceAccount{
181+
ID: state.ID.ValueString(),
182+
Name: plan.Name.ValueString(),
183+
Owner: plan.Owner.ValueString(),
184+
Scopes: scopes,
185+
CredentialLifetime: plan.CredentialLifetime.ValueInt32(),
186+
AuthenticationType: "ociToken",
187+
}
188+
189+
err := r.client.UpdateServiceAccount(registryAccount)
190+
if err != nil {
191+
resp.Diagnostics.AddError(
192+
"Error updating registryAccount",
193+
"Could not update registryAccount, unexpected error: "+err.Error(),
194+
)
195+
return
196+
}
197+
plan.ID = state.ID
198+
plan.OciAccountName = state.OciAccountName
199+
plan.OciRegistryToken = state.OciRegistryToken
200+
diags = resp.State.Set(ctx, plan)
201+
resp.Diagnostics.Append(diags...)
202+
}
203+
204+
func (r *registryAccountResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
205+
var state registryAccountResourceModel
206+
207+
diags := req.State.Get(ctx, &state)
208+
resp.Diagnostics.Append(diags...)
209+
if resp.Diagnostics.HasError() {
210+
return
211+
}
212+
213+
err := r.client.DeleteServiceAccount(state.ID.ValueString())
214+
if err != nil {
215+
resp.Diagnostics.AddError(
216+
"Error Deleting Service Account",
217+
"Could not delete Service Account ID "+state.ID.ValueString()+": "+err.Error(),
218+
)
219+
return
220+
}
221+
}
222+
223+
func (r *registryAccountResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
224+
// Retrieve import ID and save to id attribute
225+
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
226+
}

internal/tlspc/tlspc.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,8 @@ type ServiceAccount struct {
292292
CredentialLifetime int32 `json:"credentialLifetime"`
293293
PublicKey string `json:"publicKey"`
294294
AuthenticationType string `json:"authenticationType"`
295+
OciAccountName string `json:"ociAccountName"`
296+
OciRegistryToken string `json:"ociRegistryToken"`
295297
}
296298

297299
func (c *Client) CreateServiceAccount(sa ServiceAccount) (*ServiceAccount, error) {

0 commit comments

Comments
 (0)