Skip to content

Commit 19b1548

Browse files
committed
update
1 parent 66de926 commit 19b1548

File tree

8 files changed

+196
-58
lines changed

8 files changed

+196
-58
lines changed

.vscode/tasks.json

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
// See https://go.microsoft.com/fwlink/?LinkId=733558
3+
// for the documentation about the tasks.json format
4+
"version": "2.0.0",
5+
"tasks": [
6+
{
7+
"label": "hugo-new-note",
8+
"type": "shell",
9+
"command": "hugo new content/notes/${input:contentName}.md",
10+
"presentation": {
11+
"reveal": "never"
12+
},
13+
"problemMatcher": []
14+
},
15+
{
16+
"label": "hugo-new-post",
17+
"type": "shell",
18+
"command": "hugo new content/posts/${input:contentName}.md",
19+
"presentation": {
20+
"reveal": "never"
21+
},
22+
"problemMatcher": []
23+
}
24+
],
25+
"inputs": [
26+
{
27+
"id": "contentName",
28+
"type": "promptString",
29+
"description": "blabla"
30+
}
31+
]
32+
}

content/notes/test1.md

Lines changed: 0 additions & 7 deletions
This file was deleted.

content/notes/tools.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
+++
2+
date = '2026-01-15T00:25:30+05:00'
3+
draft = true
4+
title = 'Tools New'
5+
+++
6+
17
## sshuttle
28

39
Redirect traffic from local PC to private subnet:

content/notes/words.md

Lines changed: 0 additions & 42 deletions
This file was deleted.

content/posts/1-about-this.md

Lines changed: 0 additions & 7 deletions
This file was deleted.
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
+++
2+
date = '2026-02-05T19:50:51+05:00'
3+
draft = true
4+
title = 'Terraform Provider Development Experience'
5+
categories = ["Terraform", "Go"]
6+
+++
7+
8+
Developing your own terraform provider might be required in case
9+
you have some internal services. And that service and your terraform
10+
modules are used by many teams. Such cases having your own provider
11+
and distributing makes terraform code cleaner, allows to avoid glue
12+
shell scripts.
13+
14+
I did develop few providers for our internal services, below want to
15+
list points which was interesting for me, or points which I missed
16+
initially.
17+
18+
## SDK version
19+
20+
There is two SDK versions are available to develop provider: [SDKv2](https://developer.hashicorp.com/terraform/plugin/sdkv2)
21+
and [plugin framework](https://developer.hashicorp.com/terraform/plugin/framework).
22+
Later is newer and recommended by terraform, but that
23+
doesn't mean SDKv2 is deprecated, it is still widely used. Probably
24+
it will take years before all will be migrated to new one. In my case
25+
I used only plugin framework as recommended. Also there was need to
26+
read other providers which uses SDKv2, and there is no problems to do
27+
that. There is differences between them but probably because of same
28+
protocol it is easy to understand what is going on even in case of SDKv2.
29+
30+
## Pay attention to how import works
31+
32+
If you start provider development using [scaffolding app template](https://github.com/hashicorp/terraform-provider-scaffolding-framework)
33+
then you see like this code for resource:
34+
35+
```go
36+
func (r *ExampleResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
37+
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
38+
}
39+
```
40+
41+
I initially didn't pay attention much, but what is written here actually linked how
42+
you would implement your `Read` function. Basically it means when "import" is
43+
happening your `Read` function will be executed, and from fields only `id`
44+
will be available. It impacts how you define your ID, you should be able to
45+
read resource purely by ID. It sounds easy, but in some cases resource could
46+
be available only in nested way. For example, let's say you have resource
47+
by this path: `/policy/<policyID>/rules/<ruleID>`.
48+
49+
Initially you could select `ruleID` as id for your `PolicyRule` resource, which
50+
brings a problem with import. For that reason probably you should make your
51+
ID in this format `<policyID>/<ruleID>`, doing this you can fetch rule purely
52+
by ID:
53+
54+
```go
55+
func (r *ExampleResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
56+
var resourceID types.String
57+
req.State.GetAttribute(ctx, path.Root("resourceID"), &resourceID)
58+
59+
parts := strings.Split(resourceID, "/")
60+
policyID := parts[0]
61+
ruleID := parts[1]
62+
63+
// Define your http req
64+
65+
// If applicable, this is a great opportunity to initialize any necessary
66+
// provider client data and make a call using it.
67+
// httpResp, err := r.client.Do(httpReq)
68+
// if err != nil {
69+
// resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read example, got error: %s", err))
70+
// return
71+
// }
72+
73+
// Save updated data into Terraform state
74+
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
75+
}
76+
```
77+
78+
This of course means import should be run like this:
79+
80+
```
81+
terraform import <policyID>/<ruleID>
82+
```
83+
84+
## Keeping client code in same repo as your provider
85+
86+
There was recommendation like better to not keep your client code to service
87+
in same repository as your provider. But for me it was better option
88+
to avoid additional maintenance as service client wasn't going
89+
to be used by anyone else. Probably it means if your company big enough
90+
to have internal services, but not big emough to have Go sdk's for them
91+
don't hesitate to keep your service's client code in same repository.
92+
Of course keep isolation between client code and provider's (mixing them
93+
definetely not a good idea).
94+
95+
For me it simlified overall development, as service client and provider
96+
implemented in same time.
97+
98+
## Logging approach
99+
100+
In terraform structured [logging](https://developer.hashicorp.com/terraform/tutorials/providers-plugin-framework/providers-plugin-framework-logging) is used.
101+
In my case there was need to log not only from provider, but to have logs
102+
from client code as well. Also I wanted to keep some isolation (in case I would need to
103+
separate client code to separate repository). For that I implemented
104+
interface:
105+
106+
```go
107+
interface Logger {
108+
Debug(ctx context.Context, msg string)
109+
Info(ctx context.Context, msg string)
110+
}
111+
```
112+
113+
In the provider side add logger structure:
114+
115+
```go
116+
type ClientLogger struct {
117+
}
118+
119+
func (c *ClientLogger) Debug(ctx context.Context, msg string) {
120+
tflog.Debug(ctx, msg)
121+
}
122+
123+
func (c *ClientLogger) Info(ctx context.Context, msg string) {
124+
tflog.Info(ctx, msg)
125+
}
126+
```
127+
128+
In your client code add possibility to get logger as parameter:
129+
130+
```go
131+
type ExampleClient struct{
132+
Logger *ClientLogger
133+
}
134+
135+
func (c *ExampleClient) Execute(ctx context.Context) {
136+
// this log appears if TF_LOG=INFO
137+
c.Logger.Info(ctx, "Starting execute something")
138+
// do something
139+
// add more debugging logs if needed, this log appears if TF_LOG=DEBUG
140+
c.Logger.Debug(ctx, "Debugging information")
141+
}
142+
```
143+
144+
In my case logging as much as possible information in case of error
145+
was very helpful. Specially during executions of terraform in pipelines.
146+
147+
Also check diagnostics logis: https://developer.hashicorp.com/terraform/plugin/framework/diagnostics
148+
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
+++
2+
date = '2026-02-05T19:51:05+05:00'
3+
draft = true
4+
title = 'Terraform Provider Patching Experience'
5+
categories = ["Terraform", "Go"]
6+
+++
7+
8+
Coming soon

themes/til/assets/css/index.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1591,7 +1591,7 @@ footer a:hover {
15911591

15921592
.prose {
15931593
color: var(--tw-prose-body);
1594-
max-width: 65ch;
1594+
max-width: 81ch;
15951595
}
15961596

15971597
.prose :where(p):not(:where([class~='not-prose'], [class~='not-prose'] *)) {
@@ -2314,7 +2314,7 @@ footer a:hover {
23142314
}
23152315

23162316
.max-w-prose {
2317-
max-width: 65ch;
2317+
max-width: 81ch;
23182318
}
23192319

23202320
.max-w-xs {

0 commit comments

Comments
 (0)