185 lines
5.9 KiB
Markdown
185 lines
5.9 KiB
Markdown
# Add a new remote provider
|
|
|
|
A remote provider in driftctl represents a cloud provider like AWS, GitHub, GCP or Azure.
|
|
|
|
Our current architecture allows to add a new provider in a few steps.
|
|
|
|
## Declaring the new remote provider
|
|
|
|
First you need to create a new directory in `pkg/remote/<provider name>`. It will sit next to already implemented one like `pkg/remote/aws`.
|
|
|
|
Inside this directory, you will create a `init.go` file in which you will define the remote name constant:
|
|
|
|
```go
|
|
const RemoteAWSTerraform = "aws+tf"
|
|
```
|
|
|
|
`+tf` means that we use Terraform to retrieve resource's details, in the future, we may add other ways to read those details.
|
|
|
|
You will then create a function to initialize the provider and all resource's enumerators. The best way to do it would be to copy the function signature from another provider:
|
|
|
|
```go
|
|
func Init(
|
|
// Version required by the user
|
|
version string,
|
|
// Util to send alert
|
|
alerter *alerter.Alerter,
|
|
// Library that contains all providers
|
|
providerLibrary *terraform.ProviderLibrary,
|
|
// Library that contains enumerators and details fetchers for each supported resources
|
|
remoteLibrary *common.RemoteLibrary,
|
|
// Progress displayer
|
|
progress output.Progress,
|
|
// Repository for all resource schemas
|
|
resourceSchemaRepository *resource.SchemaRepository,
|
|
// Factory used to create driftctl resource
|
|
factory resource.ResourceFactory,
|
|
// driftctl configuration directory (where Terraform provider is downloaded)
|
|
configDir string) error {
|
|
|
|
// You need to define the default version of the Terraform provider when the user does not specify one
|
|
if version == "" {
|
|
version = "3.19.0"
|
|
}
|
|
|
|
// Creation of the Terraform provider
|
|
provider, err := NewAWSTerraformProvider(version, progress, configDir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// And then initialization
|
|
err = provider.Init()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// You'll need to create a new cache that will be used to cache fetched lists of resources
|
|
repositoryCache := cache.New(100)
|
|
|
|
// Deserializer is used to convert cty value returned by Terraform provider to driftctl Resource
|
|
deserializer := resource.NewDeserializer(factory)
|
|
|
|
// Adding the provider to the library
|
|
providerLibrary.AddProvider(terraform.AWS, provider)
|
|
}
|
|
```
|
|
|
|
Once done, you'll create a `provider.go` file to contain your Terraform provider representation. Again you should look at other implementation:
|
|
|
|
```go
|
|
// Define your actual provider representation, it is required to compose with terraform.TerraformProvider, a name and a version
|
|
// Please note that the name should match the real Terraform provider name.
|
|
type AWSTerraformProvider struct {
|
|
*terraform.TerraformProvider
|
|
session *session.Session
|
|
name string
|
|
version string
|
|
}
|
|
|
|
func NewAWSTerraformProvider(version string, progress output.Progress, configDir string) (*AWSTerraformProvider, error) {
|
|
// Just pass your version and name
|
|
p := &AWSTerraformProvider{
|
|
version: version,
|
|
name: "aws",
|
|
}
|
|
// Use Terraform ProviderInstaller to retrieve the provider if needed
|
|
installer, err := tf.NewProviderInstaller(tf.ProviderConfig{
|
|
Key: p.name,
|
|
Version: version,
|
|
ConfigDir: configDir,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p.session = session.Must(session.NewSessionWithOptions(session.Options{
|
|
SharedConfigState: session.SharedConfigEnable,
|
|
}))
|
|
|
|
// ProviderConfig is dependent on the Terraform provider needs.
|
|
tfProvider, err := terraform.NewTerraformProvider(installer, terraform.TerraformProviderConfig{
|
|
Name: p.name,
|
|
DefaultAlias: *p.session.Config.Region,
|
|
GetProviderConfig: func(alias string) interface{} {
|
|
return awsConfig{
|
|
Region: alias,
|
|
MaxRetries: 10,
|
|
}
|
|
},
|
|
}, progress)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p.TerraformProvider = tfProvider
|
|
return p, err
|
|
}
|
|
|
|
func (a *AWSTerraformProvider) Name() string {
|
|
return a.name
|
|
}
|
|
|
|
func (p *AWSTerraformProvider) Version() string {
|
|
return p.version
|
|
}
|
|
```
|
|
|
|
The configuration returned in `GetProviderConfig` should be annotated with `cty` tags to be passed to the provider.
|
|
|
|
```go
|
|
type githubConfig struct {
|
|
Token string
|
|
Owner string `cty:"owner"`
|
|
Organization string
|
|
}
|
|
```
|
|
|
|
You are now almost done. You'll need to make driftctl aware of this provider. Thus, the in `pkg/remote/remote.go` file, add your new constant in `supportedRemotes`:
|
|
|
|
```go
|
|
var supportedRemotes = []string{
|
|
aws.RemoteAWSTerraform,
|
|
github.RemoteGithubTerraform,
|
|
}
|
|
```
|
|
|
|
Don't forget to modify the Activate function. You'll need to add a new case in the switch statement:
|
|
|
|
```go
|
|
func Activate(remote, version string, alerter *alerter.Alerter,
|
|
providerLibrary *terraform.ProviderLibrary,
|
|
remoteLibrary *common.RemoteLibrary,
|
|
progress output.Progress,
|
|
resourceSchemaRepository *resource.SchemaRepository,
|
|
factory resource.ResourceFactory,
|
|
configDir string) error {
|
|
switch remote {
|
|
case aws.RemoteAWSTerraform:
|
|
return aws.Init(version, alerter, providerLibrary, remoteLibrary, progress, resourceSchemaRepository, factory, configDir)
|
|
case github.RemoteGithubTerraform:
|
|
return github.Init(version, alerter, providerLibrary, remoteLibrary, progress, resourceSchemaRepository, factory, configDir)
|
|
default:
|
|
return errors.Errorf("unsupported remote '%s'", remote)
|
|
}
|
|
}
|
|
```
|
|
|
|
Your provider is now set up!
|
|
|
|
## Prepare driftctl to support new resources
|
|
|
|
Each new resource of the newly added provider will be located in `pkg/resource/<provider name>` directory. You need to create the latter and the `metadatas.go` file inside it.
|
|
|
|
Inside this file add a new function:
|
|
|
|
```go
|
|
func InitResourcesMetadata(resourceSchemaRepository resource.SchemaRepositoryInterface) {
|
|
}
|
|
```
|
|
|
|
Then, add a call to this function in the `remote/<provider>/init.go` file you created in the first step.
|
|
|
|
You also need to create a test schema for upcoming tests.
|
|
|
|
Please use `TestCreateNewSchema` located in `test/schemas/schemas_test.go` to generate a schema file that will be used for the mocked provider.
|
|
|
|
Everything is now ready, you should [start adding new resources](new-resource.md)!
|