Hi I'm Ryan

Hi I'm Ryan

  • I'm a community focused Dev from Brum currently working @ Bluetel (Where you are now!)
  • I have a general interest in all things web.
  • I love programming mascots.

Disclaimer

(This talk was written in a caffeine fueled haze over the last few days.
All aboard the low standards limbo!)

Agenda

  • What is Terraform? (~5 minutes)
  • A very quick introduction to golang (~2 minutes)
  • How to put together a custom terraform provider (~10 minutes)
  • Live demo (~10 minutes)
  • Questions

To gauge the room 🌡️

Who has used here has some familiarity with Terraform?

(Or one of it's various forks)

What is Terraform?

"Infrastructure automation to provision and manage resources in any cloud or data center."

The simulation
We live in

The simulation
We live in

Our AI overlords

This concept broadly known as Infrastructure As Code

Terraform has a 63.6% percent market share
(Excluding other forks of terraform like open tofu)

 

[Firefly state of IAC 2024]

Why bother?
¯\_(ツ)_/¯

Super Important Business
Critical Server

Super Important Business
Critical Server

"Restructuring"

Super Important Business
Critical Server

Super Important Business
Critical Server

Years Pass...

Super Important Business
Critical Server

Years Pass...

Super Important Business
Critical Server

Years Pass...

Super Important Business
Critical Server

Years Pass...

Super Important Business
Critical Sever

[DO NOT TOUCH]

Super Important Business
Critical Sever

[DO NOT TOUCH]

You

S̵̯͊u̸̠͑p̴̩̓ȇ̴̗r̴͓̈́ ̴̡͐Ḯ̴̮m̷̦̃p̵̩̆o̵̢̾r̵̳͑t̸̨̂á̶̮n̴̤͘t̷̩̏ ̵͕́B̸̰͋ų̵̏s̷̝̓i̴̦̽n̸̬̈ē̴̟s̸̮̒s̴̻̊
̷̤̊C̴̜͝r̷͍̐ĭ̸͈t̵̹͒i̵͎̓ç̸̓ã̴͙ḷ̸͆ ̵̮̊S̷̯͊e̴̠̍v̸̡̒e̶̞͒r̶̲̕™̷̠̓
̸̹͋
̴̡̎[̶̨́D̴̠̈́O̸̭͑ ̵̠̃N̴͈̂Ȍ̵̺T̸͖̓ ̵̮͘T̶̢̅O̷̤͂U̵͓̍C̴̭̍H̴͖͂]̷͔̌

You

S̵̯͊u̸̠͑p̴̩̓ȇ̴̗r̴͓̈́ ̴̡͐Ḯ̴̮m̷̦̃p̵̩̆o̵̢̾r̵̳͑t̸̨̂á̶̮n̴̤͘t̷̩̏ ̵͕́B̸̰͋ų̵̏s̷̝̓i̴̦̽n̸̬̈ē̴̟s̸̮̒s̴̻̊
̷̤̊C̴̜͝r̷͍̐ĭ̸͈t̵̹͒i̵͎̓ç̸̓ã̴͙ḷ̸͆ ̵̮̊S̷̯͊e̴̠̍v̸̡̒e̶̞͒r̶̲̕™̷̠̓
̸̹͋
̴̡̎[̶̨́D̴̠̈́O̸̭͑ ̵̠̃N̴͈̂Ȍ̵̺T̸͖̓ ̵̮͘T̶̢̅O̷̤͂U̵͓̍C̴̭̍H̴͖͂]̷͔̌

You

S̵̯͊u̸̠͑p̴̩̓ȇ̴̗r̴͓̈́ ̴̡͐Ḯ̴̮m̷̦̃p̵̩̆o̵̢̾r̵̳͑t̸̨̂á̶̮n̴̤͘t̷̩̏ ̵͕́B̸̰͋ų̵̏s̷̝̓i̴̦̽n̸̬̈ē̴̟s̸̮̒s̴̻̊
̷̤̊C̴̜͝r̷͍̐ĭ̸͈t̵̹͒i̵͎̓ç̸̓ã̴͙ḷ̸͆ ̵̮̊S̷̯͊e̴̠̍v̸̡̒e̶̞͒r̶̲̕™̷̠̓
̸̹͋
̴̡̎[̶̨́D̴̠̈́O̸̭͑ ̵̠̃N̴͈̂Ȍ̵̺T̸͖̓ ̵̮͘T̶̢̅O̷̤͂U̵͓̍C̴̭̍H̴͖͂]̷͔̌

You

New Super Duper
Critical Server

Destroy

Recreate

You

New Super Duper
Critical Server

Step By Step

Step By Step

Write 📖

Plan📝

Apply 🚀

Write 📖

Writing Terraform

Hashicorp Configuration Language 🎉

resource "some_resource" "some_block" {
  some_config_block {
    bla = "bla"
    bla2 = 1337
    bla3 = false
  }
  
  something = "something"
}

Writing Terraform

Hashicorp Configuration Language 🎉

resource "some_resource" "some_block" {
  some_config_block {
    bla = "bla"
    bla2 = 1337
    bla3 = false
  }
  
  something = "something"
}

Writing Terraform

resource "aws_instance" "some_server" {
  instance_type = "t2.micro"
  [...]
}
resource "aws_instance" "some_server" {
  instance_type = "t2.micro"
  [...]
}

resource "aws_instance" "some_server2" {
  instance_type = "t2.micro"
  [...]
}
resource "aws_ami" "some_ami" {
  prefix = "someing/*"
}

resource "aws_instance" "some_server" {
  ami = aws_ami.some_ami
  instance_type = "t2.micro"
  [...]
}

resource "aws_instance" "some_server2" {
  ami = aws_ami.some_ami
  instance_type = "t2.micro"
  [...]
}

AMI

Step By Step

Write 📖

Plan📝

Apply 🚀

Plan 📖

Plan 📖

Terraform State
(What Terraform thinks you currently have)

Code Changes
(What you are declaring the next state of the system should be)

Compute
Difference

Plan 📖

terraform plan -out "tfplan"
aws.region: Reading...
aws.region: Read complete after 0s [id=us-east-1]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  - destroy

Terraform will perform the following actions:

- resource "region" "us-east-1" {
-  # The literal entire AWS Region
- }

Plan: 0 to add, 0 to change, 1 to destroy.

───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Saved the plan to: tfplan

To perform exactly these actions, run the following command to apply:
    terraform apply "tfplan"

Step By Step

Write 📖

Plan📝

Apply 🚀

Apply 🚀

Apply 🚀

$ terraform apply

Enter a value: yes

Creating... module.crowdsrike_channel_file[291]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Outputs:

update-status = "¯\_(ツ)_/¯"

Terraform Providers

[STUFF]

Provider

Terraform Providers

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

Terraform Providers

Terraform Providers

Custom Terra

form providers

Custom Terra

form providers

Custom Terra

form providers

Custom Terra

form providers

gRPC

Custom Provider
[Golang]

Custom Terra

form providers

gRPC

Custom Provider

Custom Provider
[Golang]

Custom Terra

form providers

gRPC

Custom Provider

Custom Provider
[Golang]

Custom Terra

form providers

gRPC

Custom Provider

Custom Terra

form providers

gRPC

Custom Provider

[Some other
Language]
 

(You monster)

Custom Terra

form providers

gRPC

Custom Provider

[Some other
Language]
 

(You monster)

Custom Terra

form providers

gRPC

Custom Provider

[Some other
Language]
 

(You monster)

Custom Terra

form providers

gRPC

Custom Provider

[Some other
Language]
 

(You monster)

Custom Terra

form providers

"There has been a lot of interest in writing providers using languages other than Go, and people frequently ask for information on how to go about doing that. The Terraform team's policy at this time is that while it is technically possible to write providers in languages other than Go, our tooling, documentation, and ecosystem will all assume your provider is being written in and distributed as Go code for the time being."

https://developer.hashicorp.com/terraform/plugin/best-practices/provider-code

Custom Terra

form providers

"If you are not using Go Lang Good Luck Have Fun, You are on your own"

Custom Terra

form providers

Lets go on a tangent

Custom Terra

form providers

Who here has used
Golang Before?

Custom Terra

form providers

package main

import "fmt"

func main() {
	fmt.Println("Yam Orroight.")
}

Hello World

Custom Terra

form providers

package main

import "fmt"

func main() {
	var number int = 0
    yam := "yam"
	fmt.Println(yam, "Orroight.")
}

Variables

Custom Terra

form providers

package main

import "fmt"

type MyStruct struct {
   name string
   number int
}

func NewMyStruct(name string, number int) *MyStruct {
   return &MyStruct{
	  name: name,
	  number: number,
   }
}

func main() {
	myStruct := NewMyStruct("Jeff", 25)
    fmt.Println(myStruct)
}

Structs

Custom Terra

form providers

package main

import "fmt"

type MyStruct struct {
	name   string
	number int
}

func NewMyStruct(name string, number int) *MyStruct {
	return &MyStruct{
		name:   name,
		number: number,
	}
}

// Value receiver method - doesn't modify the struct
func (m *MyStruct) GetInfo() string {
	return fmt.Sprintf("Name: %s, Number: %d", m.name, m.number)
}

func main() {
	myStruct := NewMyStruct("Jeff", 25)

	// Using value receiver method
	fmt.Println(myStruct.GetInfo()) // Name: Jeff, Number: 25
}

Methods

Custom Terra

form providers

More reading

Custom Terra

form providers

More reading

(https://gobyexample.com/)

Custom Terra

form providers

Writing custom providers

Custom Terra

form providers

Custom Terra

form providers

Terraform Providers

Custom Terra

form providers

"Providers are Terraform plugins that define resources and data sources for practitioners to use. Providers are wrapped by a provider server for interacting with Terraform."

- https://developer.hashicorp.com/terraform/plugin/framework/providers

Terraform Providers

Custom Terra

form providers

Terraform Custom Providers

Resource

Resource

Resource

Function

Data
Source

Custom Terra

form providers

Terraform Custom Providers

Resource

Resource

Resource

Function

Data
Source

Custom Terra

form providers

How providers work

Custom Terra

form providers

Custom Terra

form providers

terraform {
    required_providers {
        some_provider = {
          source = "some/provider"
          version = "0.0.1"
        }
    }
}

provider "some_provider" {
    api_key = "foobar"
}

Custom Terra

form providers

type Provider interface {
	// The name of the provider
	Metadata(context.Context, MetadataRequest, *MetadataResponse)

	// Schema configuration for the terraform provider
	Schema(context.Context, SchemaRequest, *SchemaResponse)

	// Called once for every time the provider is called to do a thing. 
    // e.g Create resources / Read the state of an app ECT..
	Configure(context.Context, ConfigureRequest, *ConfigureResponse)

	// A list of the "data sources" supported by the provider
	DataSources(context.Context) []func() datasource.DataSource

	// A list of the "resources" supported by the provider
	Resources(context.Context) []func() resource.Resource
}

Custom Terra

form providers

type Provider interface {
	// The name of the provider
	Metadata(context.Context, MetadataRequest, *MetadataResponse)

	// Schema configuration for the terraform provider
	Schema(context.Context, SchemaRequest, *SchemaResponse)

	// Called once for every time the provider is called to do a thing. 
    // e.g Create resources / Read the state of an app ECT..
	Configure(context.Context, ConfigureRequest, *ConfigureResponse)

	// A list of the "data sources" supported by the provider
	DataSources(context.Context) []func() datasource.DataSource

	// A list of the "resources" supported by the provider
	Resources(context.Context) []func() resource.Resource
}

Custom Terra

form providers

type Provider interface {
	// The name of the provider
	Metadata(context.Context, MetadataRequest, *MetadataResponse)

	// Schema configuration for the terraform provider
	Schema(context.Context, SchemaRequest, *SchemaResponse)

	// Called once for every time the provider is called to do a thing. 
    // e.g Create resources / Read the state of an app ECT..
	Configure(context.Context, ConfigureRequest, *ConfigureResponse)

	// A list of the "data sources" supported by the provider
	DataSources(context.Context) []func() datasource.DataSource

	// A list of the "resources" supported by the provider
	Resources(context.Context) []func() resource.Resource
}

Custom Terra

form providers

type Provider interface {
	// The name of the provider
	Metadata(context.Context, MetadataRequest, *MetadataResponse)

	// Schema configuration for the terraform provider
	Schema(context.Context, SchemaRequest, *SchemaResponse)

	// Called once for every time the provider is called to do a thing. 
    // e.g Create resources / Read the state of an app ECT..
	Configure(context.Context, ConfigureRequest, *ConfigureResponse)

	// A list of the "data sources" supported by the provider
	DataSources(context.Context) []func() datasource.DataSource

	// A list of the "resources" supported by the provider
	Resources(context.Context) []func() resource.Resource
}

Custom Terra

form providers

Terraform Resources

Custom Terra

form providers

resource "some_type" "some_id" {
	name = "something"
    attr = "value"
    list_vals = [
      1,
      2,
      3
    ]
}

Custom Terra

form providers

"Resources are an abstraction that allow Terraform to manage infrastructure objects, such as a compute instance, an access policy, or disk. Providers act as a translation layer between Terraform and an API, offering one or more resources for practitioners to define in a configuration."
- https://developer.hashicorp.com/terraform/plugin/framework

Custom Terra

form providers

A thing that you can:

  • Create a new thing of
  • Read the state of
  • Update the state of
  • Delete  the thing from the system

 

Custom Terra

form providers

type Resource interface {
    // Called when thing needs creating
	Create(context.Context, CreateRequest, *CreateResponse)
    
    // Called when thing needs reading
	Read(context.Context, ReadRequest, *ReadResponse)
    
    // Called when thing needs updating
	Update(context.Context, UpdateRequest, *UpdateResponse)
    
    // Called when thing needs deleting
	Delete(context.Context, DeleteRequest, *DeleteResponse)
    
    [...]
}

Custom Terra

form providers

Terraform Resources

type Resource interface {
    // Called when thing needs creating
	Create(context.Context, CreateRequest, *CreateResponse)
    
    // Called when thing needs reading
	Read(context.Context, ReadRequest, *ReadResponse)
    
    // Called when thing needs updating
	Update(context.Context, UpdateRequest, *UpdateResponse)
    
    // Called when thing needs deleting
	Delete(context.Context, DeleteRequest, *DeleteResponse)
    
    // Called to get properites the resource block should have
    // and it's behaviours
	Schema(context.Context, SchemaRequest, *SchemaResponse)
    
    // The name of the thing!
    Metadata(context.Context, MetadataRequest, *MetadataResponse)
    
    // More functionality is available in extra methods
    [...]
}

Custom Terra

form providers

Terraform Resources

type Resource interface {
    // Called when thing needs creating
	Create(context.Context, CreateRequest, *CreateResponse)
    
    // Called when thing needs reading
	Read(context.Context, ReadRequest, *ReadResponse)
    
    // Called when thing needs updating
	Update(context.Context, UpdateRequest, *UpdateResponse)
    
    // Called when thing needs deleting
	Delete(context.Context, DeleteRequest, *DeleteResponse)
    
    // Called to get properites the resource block should have
    // and it's behaviours
	Schema(context.Context, SchemaRequest, *SchemaResponse)
    
    // The name of the thing!
    Metadata(context.Context, MetadataRequest, *MetadataResponse)
    
    // More functionality is available in extra methods
    [...]
}

Custom Terra

form providers

Terraform Data Sources

Custom Terra

form providers

data "data_type" "name" {
	attr = "value"
}

// Use data elsewhere in tf file

Custom Terra

form providers

"Data sources are an abstraction that allow Terraform to reference external data. Unlike managed resources, Terraform does not manage the lifecycle of the resource or data. Data sources are intended to have no side-effects."

- https://developer.hashicorp.com/terraform/plugin/framework/data-sources

Custom Terra

form providers

Custom Terra

form providers

type DataSource interface {
	// Name of the datasource block
	Metadata(context.Context, MetadataRequest, *MetadataResponse)

	// The structure of the data source
    Schema(context.Context, SchemaRequest, *SchemaResponse)

	// Run when the data source needs refetching
	Read(context.Context, ReadRequest, *ReadResponse)
}

Custom Terra

form providers

type DataSource interface {
	Metadata([...])
    Schema([...])
	Read([...])
}
type Resource interface {
	Create([...])
	Read([...])
	Update([...])
	Delete([...])
	Schema([...])
    Metadata([...])
    [...]
}

Custom Terra

form providers

type DataSource interface {
	Metadata([...])
    Schema([...])
	Read([...])
}
type Resource interface {
	Create([...])
	Read([...])
	Update([...])
	Delete([...])
	Schema([...])
    Metadata([...])
    [...]
}

Data sources act in a similar capacity to a "Read-Only" resource

Custom Terra

form providers

Functions

"Functions are an abstraction that allow providers to expose computational logic beyond Terraform's built-in functions and simplify practitioner configurations"

- https://developer.hashicorp.com/terraform/plugin/framework/functions 

Function.

It's a function

Custom Terra

form providers

locals {
   two = provider::provider_name::add(1, 1)
}

Custom Terra

form providers

type Function interface {
	// The name of the function
	Metadata(context.Context, MetadataRequest, *MetadataResponse)
	
    // Defines the arguments it accept's and it's return values
	Definition(context.Context, DefinitionRequest, *DefinitionResponse)
	
    // The code to execute when the function it run
	Run(context.Context, RunRequest, *RunResponse)
}

Custom Terra

form providers

Schemas

Custom Terra

form providers

func (d *LightDataSource) Schema(
	_ context.Context,
    req datasource.SchemaRequest,
    resp *datasource.SchemaResponse
) {
	resp.Schema = schema.Schema{
		Attributes: map[string]schema.Attribute{
            "id": schema.StringAttribute{
				Computed: true,
			},
			"value": schema.StringAttribute{
				Required: true,
			},
			"some_other_value": schema.BoolAttribute{
				Optional: true
			},
		},
	}
}

Custom Terra

form providers

func (d *LightDataSource) Schema(
	_ context.Context,
    req datasource.SchemaRequest,
    resp *datasource.SchemaResponse
) {
	resp.Schema = schema.Schema{
		Attributes: map[string]schema.Attribute{
            "id": schema.StringAttribute{
				Computed: true,
			},
			"value": schema.StringAttribute{
				Required: true,
			},
			"some_other_value": schema.BoolAttribute{
				Optional: true
			},
		},
	}
}
datasource "some_name" "some_value" {
	value = "huh"
    some_other_value = "yay"
}

Custom Terra

form providers

Configuring Clients

Custom Terra

form providers

Custom Terra

form providers

Live Demo

Custom Terra

form providers

Live Demo

Custom Terra

form providers

Further Reading

https://developer.hashicorp.com/terraform/plugin/framework

Custom Terra

form providers

Provider Testing

Advanced Use Cases for Resources (Import)

Provider Documentation Generation

Much much more!

Provider Registry Publishing

What we have covered in this talk

Custom Terra

form providers

Credits

Wikimedia [CC0, CC-SA4]

@_Iroshi (Gophers CC0)

Custom Terra

form providers

Questions?

Custom Terra

form providers

Thank you!

You can find my socials on
https//ryan.gd

Hashicorp User Group 2025

By Rizza

Hashicorp User Group 2025

  • 54