github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/website/docs/plugins/provider.html.md (about)

     1  ---
     2  layout: "docs"
     3  page_title: "Provider Plugins"
     4  sidebar_current: "docs-plugins-provider"
     5  description: |-
     6    A provider in Terraform is responsible for the lifecycle of a resource: create, read, update, delete. An example of a provider is AWS, which can manage resources of type `aws_instance`, `aws_eip`, `aws_elb`, etc.
     7  ---
     8  
     9  # Provider Plugins
    10  
    11  ~> **Advanced topic!** Plugin development is a highly advanced
    12  topic in Terraform, and is not required knowledge for day-to-day usage.
    13  If you don't plan on writing any plugins, this section of the documentation is 
    14  not necessary to read. For general use of Terraform, please see our
    15  [Intro to Terraform](/intro/index.html) and [Getting
    16  Started](https://learn.hashicorp.com/terraform/getting-started/install) guides.
    17  
    18  A provider in Terraform is responsible for the lifecycle of a resource:
    19  create, read, update, delete. An example of a provider is AWS, which
    20  can manage resources of type `aws_instance`, `aws_eip`, `aws_elb`, etc.
    21  
    22  The primary reasons to care about provider plugins are:
    23  
    24    * You want to add a new resource type to an existing provider.
    25  
    26    * You want to write a completely new provider for managing resource
    27      types in a system not yet supported.
    28  
    29    * You want to write a completely new provider for custom, internal
    30      systems such as a private inventory management system.
    31  
    32  If you're interested in provider development, then read on. The remainder
    33  of this page will assume you're familiar with
    34  [plugin basics](/docs/plugins/basics.html) and that you already have
    35  a basic development environment setup.
    36  
    37  ## Provider Plugin Codebases
    38  
    39  Provider plugins live outside of the Terraform core codebase in their own
    40  source code repositories. The official set of provider plugins released by
    41  HashiCorp (developed by both HashiCorp staff and community contributors)
    42  all live in repositories in
    43  [the `terraform-providers` organization](https://github.com/terraform-providers)
    44  on GitHub, but third-party plugins can be maintained in any source code
    45  repository.
    46  
    47  When developing a provider plugin, it is recommended to use a common `GOPATH`
    48  that includes both the core Terraform repository and the repositories of any
    49  providers being changed. This makes it easier to use a locally-built
    50  `terraform` executable and a set of locally-built provider plugins together
    51  without further configuration.
    52  
    53  For example, to download both Terraform and the `template` provider into
    54  `GOPATH`:
    55  
    56  ```
    57  $ go get github.com/hashicorp/terraform
    58  $ go get github.com/terraform-providers/terraform-provider-template
    59  ```
    60  
    61  These two packages are both "main" packages that can be built into separate
    62  executables with `go install`:
    63  
    64  ```
    65  $ go install github.com/hashicorp/terraform
    66  $ go install github.com/terraform-providers/terraform-provider-template
    67  ```
    68  
    69  After running the above commands, both Terraform core and the `template`
    70  provider will both be installed in the current `GOPATH` and `$GOPATH/bin`
    71  will contain both `terraform` and `terraform-provider-template` executables.
    72  This `terraform` executable will find and use the `template` provider plugin
    73  alongside it in the `bin` directory in preference to downloading and installing
    74  an official release.
    75  
    76  When constructing a new provider from scratch, it's recommended to follow
    77  a similar repository structure as for the existing providers, with the main
    78  package in the repository root and a library package in a subdirectory named
    79  after the provider. For more information, see
    80  [Writing Custom Providers](/docs/extend/writing-custom-providers.html) in the
    81  [Extending Terraform section](/docs/extend/index.html).
    82  
    83  When making changes only to files within the provider repository, it is _not_
    84  necessary to re-build the main Terraform executable. Note that some packages
    85  from the Terraform repository are used as library dependencies by providers,
    86  such as `github.com/hashicorp/terraform/helper/schema`; it is recommended to
    87  use `govendor` to create a local vendor copy of the relevant packages in the
    88  provider repository, as can be seen in the repositories within the
    89  `terraform-providers` GitHub organization.
    90  
    91  ## Low-Level Interface
    92  
    93  The interface you must implement for providers is
    94  [ResourceProvider](https://github.com/hashicorp/terraform/blob/master/terraform/resource_provider.go).
    95  
    96  This interface is extremely low level, however, and we don't recommend
    97  you implement it directly. Implementing the interface directly is error
    98  prone, complicated, and difficult.
    99  
   100  Instead, we've developed some higher level libraries to help you out
   101  with developing providers. These are the same libraries we use in our
   102  own core providers.
   103  
   104  ## helper/schema
   105  
   106  The `helper/schema` library is a framework we've built to make creating
   107  providers extremely easy. This is the same library we use to build most
   108  of the core providers.
   109  
   110  To give you an idea of how productive you can become with this framework:
   111  we implemented the Google Cloud provider in about 6 hours of coding work.
   112  This isn't a simple provider, and we did have knowledge of
   113  the framework beforehand, but it goes to show how expressive the framework
   114  can be.
   115  
   116  The GoDoc for `helper/schema` can be
   117  [found here](https://godoc.org/github.com/hashicorp/terraform/helper/schema).
   118  This is API-level documentation but will be extremely important
   119  for you going forward.
   120  
   121  ## Provider
   122  
   123  The first thing to do in your plugin is to create the
   124  [schema.Provider](https://godoc.org/github.com/hashicorp/terraform/helper/schema#Provider) structure.
   125  This structure implements the `ResourceProvider` interface. We
   126  recommend creating this structure in a function to make testing easier
   127  later. Example:
   128  
   129  ```golang
   130  func Provider() *schema.Provider {
   131  	return &schema.Provider{
   132  		...
   133  	}
   134  }
   135  ```
   136  
   137  Within the `schema.Provider`, you should initialize all the fields. They
   138  are documented within the godoc, but a brief overview is here as well:
   139  
   140    * `Schema` - This is the configuration schema for the provider itself.
   141        You should define any API keys, etc. here. Schemas are covered below.
   142  
   143    * `ResourcesMap` - The map of resources that this provider supports.
   144        All keys are resource names and the values are the
   145        [schema.Resource](https://godoc.org/github.com/hashicorp/terraform/helper/schema#Resource) structures implementing this resource.
   146  
   147    * `ConfigureFunc` - This function callback is used to configure the
   148        provider. This function should do things such as initialize any API
   149        clients, validate API keys, etc. The `interface{}` return value of
   150        this function is the `meta` parameter that will be passed into all
   151        resource [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete)
   152        functions. In general, the returned value is a configuration structure
   153        or a client.
   154  
   155  As part of the unit tests, you should call `InternalValidate`. This is used
   156  to verify the structure of the provider and all of the resources, and reports
   157  an error if it is invalid. An example test is shown below:
   158  
   159  ```golang
   160  func TestProvider(t *testing.T) {
   161  	if err := Provider().(*schema.Provider).InternalValidate(); err != nil {
   162  		t.Fatalf("err: %s", err)
   163  	}
   164  }
   165  ```
   166  
   167  Having this unit test will catch a lot of beginner mistakes as you build
   168  your provider.
   169  
   170  ## Resources
   171  
   172  Next, you'll want to create the resources that the provider can manage.
   173  These resources are put into the `ResourcesMap` field of the provider
   174  structure. Again, we recommend creating functions to instantiate these.
   175  An example is shown below.
   176  
   177  ```golang
   178  func resourceComputeAddress() *schema.Resource {
   179  	return &schema.Resource {
   180  		...
   181  	}
   182  }
   183  ```
   184  
   185  Resources are described using the
   186  [schema.Resource](https://godoc.org/github.com/hashicorp/terraform/helper/schema#Resource)
   187  structure. This structure has the following fields:
   188  
   189    * `Schema` - The configuration schema for this resource. Schemas are
   190        covered in more detail below.
   191  
   192    * `Create`, `Read`, `Update`, and `Delete` - These are the callback
   193        functions that implement CRUD operations for the resource. The only
   194        optional field is `Update`. If your resource doesn't support update, then
   195        you may keep that field nil.
   196  
   197    * `Importer` - If this is non-nil, then this resource is
   198      [importable](/docs/import/importability.html). It is recommended to
   199      implement this.
   200  
   201  The CRUD operations in more detail, along with their contracts:
   202  
   203    * `Create` - This is called to create a new instance of the resource.
   204        Terraform guarantees that an existing ID is not set on the resource
   205        data. That is, you're working with a new resource. Therefore, you are
   206        responsible for calling `SetId` on your `schema.ResourceData` using a
   207        value suitable for your resource. This ensures whatever resource
   208        state you set on `schema.ResourceData` will be persisted in local state.
   209        If you neglect to `SetId`, no resource state will be persisted.
   210  
   211    * `Read` - This is called to resync the local state with the remote state.
   212        Terraform guarantees that an existing ID will be set. This ID should be
   213        used to look up the resource. Any remote data should be updated into
   214        the local data. **No changes to the remote resource are to be made.**
   215        If the resource is no longer present, calling `SetId`
   216        with an empty string will signal its removal.
   217  
   218    * `Update` - This is called to update properties of an existing resource.
   219        Terraform guarantees that an existing ID will be set. Additionally,
   220        the only changed attributes are guaranteed to be those that support
   221        update, as specified by the schema. Be careful to read about partial
   222        states below.
   223  
   224    * `Delete` - This is called to delete the resource. Terraform guarantees
   225        an existing ID will be set.
   226  
   227    * `Exists` - This is called to verify a resource still exists. It is
   228        called prior to `Read`, and lowers the burden of `Read` to be able
   229        to assume the resource exists. `false` should be returned if
   230        the resources is no longer present, which has the same effect
   231        as calling `SetId("")` from `Read` (i.e. removal of the resource data
   232        from state).
   233  
   234  ## Schemas
   235  
   236  Both providers and resources require a schema to be specified. The schema
   237  is used to define the structure of the configuration, the types, etc. It is
   238  very important to get correct.
   239  
   240  In both provider and resource, the schema is a `map[string]*schema.Schema`.
   241  The key of this map is the configuration key, and the value is a schema for
   242  the value of that key.
   243  
   244  Schemas are incredibly powerful, so this documentation page won't attempt
   245  to cover the full power of them. Instead, the API docs should be referenced
   246  which cover all available settings.
   247  
   248  We recommend viewing schemas of existing or similar providers to learn
   249  best practices. A good starting place is the
   250  [core Terraform providers](https://github.com/terraform-providers).
   251  
   252  ## Resource Data
   253  
   254  The parameter to provider configuration as well as all the CRUD operations
   255  on a resource is a
   256  [schema.ResourceData](https://godoc.org/github.com/hashicorp/terraform/helper/schema#ResourceData).
   257  This structure is used to query configurations as well as to set information
   258  about the resource such as its ID, connection information, and computed
   259  attributes.
   260  
   261  The API documentation covers ResourceData well, as well as the core providers
   262  in Terraform.
   263  
   264  **Partial state** deserves a special mention. Occasionally in Terraform, create or
   265  update operations are not atomic; they can fail halfway through. As an example,
   266  when creating an AWS security group, creating the group may succeed,
   267  but creating all the initial rules may fail. In this case, it is incredibly
   268  important that Terraform record the correct _partial state_ so that a
   269  subsequent `terraform apply` fixes this resource.
   270  
   271  Most of the time, partial state is not required. When it is, it must be
   272  specifically enabled. An example is shown below:
   273  
   274  ```golang
   275  func resourceUpdate(d *schema.ResourceData, meta interface{}) error {
   276  	// Enable partial state mode
   277  	d.Partial(true)
   278  
   279  	if d.HasChange("tags") {
   280  		// If an error occurs, return with an error,
   281  		// we didn't finish updating
   282  		if err := updateTags(d, meta); err != nil {
   283  			return err
   284  		}
   285  
   286  		d.SetPartial("tags")
   287  	}
   288  
   289  	if d.HasChange("name") {
   290  		if err := updateName(d, meta); err != nil {
   291  			return err
   292  		}
   293  
   294  		d.SetPartial("name")
   295  	}
   296  
   297  	// We succeeded, disable partial mode
   298  	d.Partial(false)
   299  
   300  	return nil
   301  }
   302  ```
   303  
   304  In the example above, it is possible that setting the `tags` succeeds,
   305  but setting the `name` fails. In this scenario, we want to make sure
   306  that only the state of the `tags` is updated. To do this the
   307  `Partial` and `SetPartial` functions are used.
   308  
   309  `Partial` toggles partial-state mode. When disabled, all changes are merged
   310  into the state upon result of the operation. When enabled, only changes
   311  enabled with `SetPartial` are merged in.
   312  
   313  `SetPartial` tells Terraform what state changes to adopt upon completion
   314  of an operation. You should call `SetPartial` with every key that is safe
   315  to merge into the state. The parameter to `SetPartial` is a prefix, so
   316  if you have a nested structure and want to accept the whole thing,
   317  you can just specify the prefix.