diff --git a/.gitignore b/.gitignore
index 66fd13c..14fdb8f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,5 +11,8 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
-# Dependency directories (remove the comment below to include it)
-# vendor/
+# Dependency directories
+vendor/
+
+#generated outputs
+internal/
\ No newline at end of file
diff --git a/.go-version b/.go-version
new file mode 100644
index 0000000..de646d2
--- /dev/null
+++ b/.go-version
@@ -0,0 +1 @@
+1.16.6
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..73f69e0
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..22d364a
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/tfpdk.iml b/.idea/tfpdk.iml
new file mode 100644
index 0000000..5e764c4
--- /dev/null
+++ b/.idea/tfpdk.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index cc2d6fb..c436c34 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,11 @@
# tfpdk
Terraform Provider Development Kit
+
+## TODO
+- [ ] untyped resource template
+- [ ] `go fmt` outputs
+- [ ] typed and untyped Data Sources
+- [ ] init a new provider - git clone [scaffold](https://github.com/hashicorp/terraform-provider-scaffolding)?
+- [ ] optionally populate the Typed SDK into the init step?
+- [ ] Dynamically read provider name for item generation
+- [ ] Populate `IDValidationFunc()` in template for IDs (Pandora)
diff --git a/commands/common.go b/commands/common.go
new file mode 100644
index 0000000..252e30d
--- /dev/null
+++ b/commands/common.go
@@ -0,0 +1,17 @@
+package commands
+
+import (
+ "strings"
+ "text/template"
+
+ "github.com/iancoleman/strcase"
+ "github.com/jackofallops/tfpdk/helpers"
+)
+
+var TplFuncMap = template.FuncMap{
+ "ToLower": strings.ToLower,
+ "ToTitle": strings.Title,
+ "ToCamel": strcase.ToCamel,
+ "ToSnake": strcase.ToSnake,
+ "TfName": helpers.TerraformResourceName,
+}
diff --git a/commands/initialise.go b/commands/initialise.go
new file mode 100644
index 0000000..85bf852
--- /dev/null
+++ b/commands/initialise.go
@@ -0,0 +1,16 @@
+package commands
+
+type InitialiseCommand struct{}
+
+func (i InitialiseCommand) Run(args []string) int {
+ // TODO - Clone the scaffold project to path from `args`?
+ return 0
+}
+
+func (InitialiseCommand) Help() string {
+ return ""
+}
+
+func (InitialiseCommand) Synopsis() string {
+ return ""
+}
diff --git a/commands/resource.go b/commands/resource.go
new file mode 100644
index 0000000..acc1371
--- /dev/null
+++ b/commands/resource.go
@@ -0,0 +1,107 @@
+package commands
+
+import (
+ "fmt"
+ "os"
+ "strings"
+ "text/template"
+
+ "github.com/iancoleman/strcase"
+)
+
+type ResourceCommand struct {
+}
+
+type TypedResourceData struct {
+ Name string
+ ServicePackage string
+ NoUpdate bool
+ ProviderName string
+ Typed bool
+}
+
+func (r ResourceCommand) Run(args []string) int {
+ data := TypedResourceData{
+ ProviderName: "azurerm", // Defaulting for now, will need to eval from os.path later
+ Typed: true, //defaulting to true for now, since AzureRM is the prototype target
+ }
+ if len(args) == 0 {
+ fmt.Print(r.Help())
+ return 1
+ }
+ for _, v := range args {
+ arg := strings.Split(v, "=")
+ if len(arg) > 2 {
+ fmt.Printf("malformed argument %q", arg)
+ return 1
+ }
+
+ switch strings.ToLower(strings.TrimLeft(arg[0], "-")) {
+ case "name":
+ data.Name = arg[1]
+ case "no-update":
+ data.NoUpdate = true
+ case "servicepackage":
+ data.ServicePackage = arg[1]
+ case "typed":
+ data.Typed = true
+ default:
+ fmt.Printf("unrecognised option %q", arg[0])
+ return 1
+ }
+ }
+
+ tpl := template.Must(template.New("resource.gotpl").Funcs(TplFuncMap).ParseFS(Templatedir, "templates/resource.gotpl"))
+
+ // output file - Note we assume that the PATH to the file already exists, having been through init and, optionally, service package creation
+ outputPath := ""
+ if data.ServicePackage != "" {
+ outputPath = fmt.Sprintf("%s/internal/services/%s/%s_resource.go", strings.ToLower(data.ProviderName), strings.ToLower(strcase.ToCamel(data.ServicePackage)), strcase.ToSnake(data.Name))
+ } else {
+ outputPath = fmt.Sprintf("%s/internal/%s_resource.go", data.ProviderName, strcase.ToSnake(data.Name))
+ }
+
+ if _, err := os.Stat(outputPath); err == nil {
+ fmt.Printf("Error: A resource with this name already exists and will not be overwritten. Please remove this file if you wish to regenerate.")
+ return 1
+ }
+ f, err := os.Create(outputPath)
+ if err != nil {
+ fmt.Printf("Error: failed opening output resource file for writing: %+v", err.Error())
+ return 1
+ }
+
+ err = tpl.Execute(f, data)
+ if err != nil {
+ fmt.Println(err.Error())
+ return 1
+ }
+ if err := f.Close(); err != nil {
+ fmt.Printf("Error: Writing to file: %+v", err.Error())
+ }
+
+ return 0
+}
+
+func (r ResourceCommand) Help() string {
+ return fmt.Sprintf(`
+Usage: tfpdk resource [options]
+
+Generates a scaffolded resource, optionally under a service package for this provider
+
+Options:
+
+-name=string (Required) the name of the new resource, can be in the form resource_name, ResourceName, or resource-name
+
+-servicepackage=string (Optional) place the resource under the named service package
+
+-no-update (Optional) Don't generate an update func. Use this for resources that cannot be updated in place. Note all schema properties must be 'ForceNew: true'
+
+-typed (Optional) Generate a resource for use with the Typed Resource SDK
+
+`)
+}
+
+func (r ResourceCommand) Synopsis() string {
+ return ""
+}
diff --git a/commands/templatefs.go b/commands/templatefs.go
new file mode 100644
index 0000000..df4b5a0
--- /dev/null
+++ b/commands/templatefs.go
@@ -0,0 +1,9 @@
+package commands
+
+import (
+ "embed"
+)
+
+//go:embed templates/*
+var Templatedir embed.FS
+
diff --git a/commands/templates/resource.gotpl b/commands/templates/resource.gotpl
new file mode 100644
index 0000000..2f7029b
--- /dev/null
+++ b/commands/templates/resource.gotpl
@@ -0,0 +1,90 @@
+package {{if .Typed}} {{- ToLower .ServicePackage}}
+
+import (
+ "context"
+ "time"
+
+ "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/sdk"
+ "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/pluginsdk"
+)
+
+type {{ToCamel .Name}}Resource struct{}
+
+type {{ToCamel .Name}}Model struct {
+ // TODO - Schema in Go Types format here
+}
+
+{{if .NoUpdate}}var _ sdk.Resource = {{else}}var _ sdk.ResourceWithUpdate = {{end}}{{ToCamel .Name}}Resource{}
+
+func (r {{ToCamel .Name}}Resource) ModelObject() interface{} {
+ return {{ToCamel .Name}}Model{}
+}
+
+func (r {{ToCamel .Name}}Resource) ResourceType() string {
+ return "{{- TfName .ProviderName .Name -}}"
+}
+
+func (r {{ToCamel .Name}}Resource) IDValidationFunc() pluginsdk.SchemaValidateFunc {
+ panic("Implement me") // TODO - Add Validation func return here
+}
+
+func (r {{ToCamel .Name}}Resource) Arguments() map[string]*pluginsdk.Schema {
+ return map[string]*pluginsdk.Schema{
+ /*
+ TODO - This sections is for configurable items, `Required: true` items first, followed by`Optional: true`,
+ both in alphabetical order
+ */
+ }
+}
+
+func (r {{ToCamel .Name}}Resource) Attributes() map[string]*pluginsdk.Schema {
+ return map[string]*pluginsdk.Schema{
+ /*
+ TODO - This section is for `Computed: true` only items, i.e. useful values that are returned by the resource
+ that can be used as outputs or passed programmatically to other resources or data sources.
+ */
+ }
+}
+
+func (r {{ToCamel .Name}}Resource) Create() sdk.ResourceFunc {
+ return sdk.ResourceFunc{
+ Timeout: 30 * time.Minute,
+ Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
+ // TODO - Create Func
+ return nil
+ },
+ }
+}
+
+func (r {{ToCamel .Name}}Resource) Read() sdk.ResourceFunc {
+ return sdk.ResourceFunc{
+ Timeout: 5 * time.Minute,
+ Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
+ // TODO - Read Func
+ return nil
+ },
+ }
+}
+
+func (r {{ToCamel .Name}}Resource) Delete() sdk.ResourceFunc {
+ return sdk.ResourceFunc{
+ Timeout: 5 * time.Minute,
+ Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
+ // TODO - Delete Func
+ return nil
+ },
+ }
+}
+{{if not .NoUpdate}}
+func (r {{ToCamel .Name}}Resource) Update() sdk.ResourceFunc {
+ return sdk.ResourceFunc{
+ Timeout: 30 * time.Minute,
+ Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
+ // TODO - Delete Func
+ return nil
+ },
+ }
+}
+{{end}}
+{{else}} {{- ToLower .ProviderName}} {{end}}
+{{/* TODO - Untyped resource */}}
\ No newline at end of file
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..4cfe692
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,8 @@
+module github.com/jackofallops/tfpdk
+
+go 1.16
+
+require (
+ github.com/iancoleman/strcase v0.2.0
+ github.com/mitchellh/cli v1.1.2
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..345183c
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,56 @@
+github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg=
+github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
+github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
+github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
+github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60=
+github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
+github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to=
+github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
+github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
+github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
+github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
+github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
+github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0=
+github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
+github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
+github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
+github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
+github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
+github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mitchellh/cli v1.1.2 h1:PvH+lL2B7IQ101xQL63Of8yFS2y+aDlsFcsqNc+u/Kw=
+github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4=
+github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
+github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
+github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
+github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w=
+github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
+golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/helpers/formatters.go b/helpers/formatters.go
new file mode 100644
index 0000000..dbcd55c
--- /dev/null
+++ b/helpers/formatters.go
@@ -0,0 +1,13 @@
+package helpers
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/iancoleman/strcase"
+)
+
+func TerraformResourceName(provider, resourceName string) string {
+ fmtStr := "%s_%s"
+ return fmt.Sprintf(fmtStr, strings.ToLower(provider), strcase.ToSnake(resourceName))
+}
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..03e4553
--- /dev/null
+++ b/main.go
@@ -0,0 +1,37 @@
+package main
+
+import (
+ "log"
+ "os"
+
+ "github.com/jackofallops/tfpdk/commands"
+ "github.com/mitchellh/cli"
+)
+
+var Commands map[string]cli.CommandFactory
+
+func main() {
+ args := os.Args[1:]
+ Commands = map[string]cli.CommandFactory{
+ "init": func() (cli.Command, error) {
+ return &commands.InitialiseCommand{}, nil
+ },
+ "resource": func() (cli.Command, error) {
+ return &commands.ResourceCommand{}, nil
+ },
+ }
+
+ tfpdk := cli.CLI{
+ Args: args,
+ Commands: Commands,
+ Name: "tfpdk",
+ Version: "0.1.0",
+ }
+
+ exitStatus, err := tfpdk.Run()
+ if err != nil {
+ log.Println(err)
+ }
+
+ os.Exit(exitStatus)
+}