This custom terraform provider is used to manage resources for ElvID, which is an Elvia application that uses IdentityServer.
The provider is published to registry.terraform.io/providers/3lvia/elvid.
It can be used to manage machine clients (client_credentials/password), user clients (authorization code), ClientSecrets for these clients and API scopes. It uses Azure AD Service Principal for authentication, and on the API side we require a specific scope for authorization.
Note that naming of resources, the authentication/authorization, and some schema variables (and their defaults) might be specific to Elvia's use case. Still we hope you can use this repo for inspiration and as a base to create your custom provider to manage IdentityServer resources.
To implement this in your IdentityServer solution you also need to create the API that receives these requests and updates your ConfigurationStore.
See here for the general information about creating custom providers from Terraform.
Install terraform. For Windows, you can add terraform.exe to {user}/bin. Make sure %USERPROFILE%\go\bin is in path, and above the go-specific paths.
Checkout the code-repo to {GOPATH}\src\github.com\3lvia\terraform-provider-elvid
- repo-root
- internal
- provider: classes for elvid-provider and resources (machineclient, userclient, clientsecret and apiscope)
- elvidapiclient: classes for getting AccessToken from AD and calling ElvID-api
- terraform-tester.tf and versions.tf: Terraform files for manually testing the provider.
- elvidapiclient: go class library for getting AccessToken from AD and calling ElvID-api
- main.go: Standard file, sets up serving of the provider.
- internal
This makes sure that terraform will use the local build of the provider and not the published build from terraform registry.
Note that terraform init will download the published library from terraform registry, but the dev_overrides variant will still be used on plan/apply.
This is done in the .terraformrc/terraform.rc file see https://www.terraform.io/cli/config/config-file#locations
For Windows create/edit $env:APPDATA\terraform.rc and add provider_installation
provider_installation {
# Override provider for local development
dev_overrides {
"3lvia/elvid" = "C:\\Users\\{{replace with your Windows username}}\\go\\src\\github.com\\3lvia\\terraform-provider-elvid"
}
# For all other providers, install them directly from their origin provider
# registries as normal. If you omit this, Terraform will _only_ use
# the dev_overrides block, and so no other providers will be available.
direct {}
}
Create {repo-root}/terraform.tfvars and add the content found in vault-dev in the path /elvid/kv/manual/terraform-provider-elvid-local-credentials with key terraform.tfvars
That will look something like
terraform_sp_client_id = "replaceme"
terraform_sp_client_secret = "replaceme"
tenant_id = "replaceme"
elvid_dev_vault_role_id = "replaceme"
Note that terraform.tfvars is added to .gitignore. Make sure to never publish these secrets. This is a public repository.
# from repo-root
go build
Make sure you have set up Terraform for running locally (described above)
# from repo-root
terraform apply;
# Similar with auto apply
terraform apply -auto-approve;
You should get a warning on plan / apply
The following provider development overrides are set in the CLI configuration:
│ - 3lvia/elvid in C:\Users\{{username}}\go\src\github.com\3lvia\terraform-provider-elvid
You don't usually need to run terraform init because we are using dev_overrides. If you are working with modules, you might have to do terraform init (it will tell you when running plan or apply). Terraform init will download the published library from terraform registry, but the dev_overrides variant will still be used on plan/apply.
Providers use Diagnostics to surface errors and warnings to Terraform. For debugging or informational purposes use logging instead.
More info about diagnostics.
Regular logging can be done with tflog with methods for Debug, Info, Warn and Error. More info about logging.
Example logging string
tflog.Warn(ctx, "Some message to be logged")
Example logging object as json
serialized, _ := json.Marshal(someObject)
tflog.Warn(ctx, string(serialized))
To see Debug and Info logs in Terraform Enterprise: Start a new run --> Additional planning options --> Enable debugging mode
If you don't se the logs locally, you probably need to change log level first.
# Linux
TF_LOG=INFO
# Windows:
$Env:TF_LOG="INFO"
Debugging is now supported (but not tested by us): https://developer.hashicorp.com/terraform/plugin/framework/debugging
Up til now, we have only used logging to understand what is goin on during a run.
To publish to registry.terraform.io/providers/3lvia/elvid create a new github-release in this repo. Github-actions automatically builds and publishes new releases.
Github-actions uses our private signing key to sign the build. The public variant of this key is added in terraform registry. Backup of the key is found in vault (prod) elvid/kv/manual/elvid-provider-build-signing-key
- Creating a class-library to wrap the api was helpful, to get more clean resource-code. It was beneficial to have it in the same repo.
- The code must be in {GOPATH}\src\github.com\3lvia
- Resource filenames must follow the format resource_{resourcename}.go
- Creating a "resource_taint_version" variable with ForceNew=true was very helpful to quickly test changes, and will be helpful in actual use as well.
- Terraform does handle state and knows when to call create, read, delete, update. So create good variable-schemas, implement these methods and let terraform handle the rest.
- The id field has to be Optional and Computed, so even resources where the id can be defined in the tf file, it will be "(known after apply)". Example: apiscope, where id=name, and we use the name field as required input, and set id=name when the resource is created.