diff --git a/backend/remote-state/azure/backend.go b/backend/remote-state/azure/backend.go index de7b6aadbbd3..d340048bdd8b 100644 --- a/backend/remote-state/azure/backend.go +++ b/backend/remote-state/azure/backend.go @@ -51,6 +51,13 @@ func New() backend.Backend { DefaultFunc: schema.EnvDefaultFunc("ARM_SAS_TOKEN", ""), }, + "snapshot": { + Type: schema.TypeBool, + Optional: true, + Description: "Enable/Disable automatic blob snapshotting", + DefaultFunc: schema.EnvDefaultFunc("ARM_SNAPSHOT", false), + }, + "resource_group_name": { Type: schema.TypeString, Optional: true, @@ -150,6 +157,7 @@ type Backend struct { containerName string keyName string accountName string + snapshot bool } type BackendConfig struct { @@ -180,6 +188,7 @@ func (b *Backend) configure(ctx context.Context) error { b.containerName = data.Get("container_name").(string) b.accountName = data.Get("storage_account_name").(string) b.keyName = data.Get("key").(string) + b.snapshot = data.Get("snapshot").(bool) // support for previously deprecated fields clientId := valueFromDeprecatedField(data, "client_id", "arm_client_id") diff --git a/backend/remote-state/azure/backend_state.go b/backend/remote-state/azure/backend_state.go index 02f89c9f92cc..07c15ffdcd63 100644 --- a/backend/remote-state/azure/backend_state.go +++ b/backend/remote-state/azure/backend_state.go @@ -90,6 +90,7 @@ func (b *Backend) StateMgr(name string) (state.State, error) { containerName: b.containerName, keyName: b.path(name), accountName: b.accountName, + snapshot: b.snapshot, } stateMgr := &remote.State{Client: client} diff --git a/backend/remote-state/azure/backend_test.go b/backend/remote-state/azure/backend_test.go index 09ce9e8e22cc..1753c6358a13 100644 --- a/backend/remote-state/azure/backend_test.go +++ b/backend/remote-state/azure/backend_test.go @@ -21,6 +21,7 @@ func TestBackendConfig(t *testing.T) { "storage_account_name": "tfaccount", "container_name": "tfcontainer", "key": "state", + "snapshot": false, // Access Key must be Base64 "access_key": "QUNDRVNTX0tFWQ0K", } @@ -33,6 +34,9 @@ func TestBackendConfig(t *testing.T) { if b.keyName != "state" { t.Fatalf("Incorrect keyName was populated") } + if b.snapshot != false { + t.Fatalf("Incorrect snapshot was populated") + } } func TestBackendAccessKeyBasic(t *testing.T) { diff --git a/backend/remote-state/azure/client.go b/backend/remote-state/azure/client.go index 68dff2d9a4f4..23dcba3a4f56 100644 --- a/backend/remote-state/azure/client.go +++ b/backend/remote-state/azure/client.go @@ -5,11 +5,14 @@ import ( "encoding/base64" "encoding/json" "fmt" + "log" + "github.com/hashicorp/go-multierror" "github.com/hashicorp/go-uuid" + "github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/blobs" + "github.com/hashicorp/terraform/state" "github.com/hashicorp/terraform/state/remote" - "github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/blobs" ) const ( @@ -24,6 +27,7 @@ type RemoteClient struct { containerName string keyName string leaseID string + snapshot bool } func (c *RemoteClient) Get() (*remote.Payload, error) { @@ -67,6 +71,18 @@ func (c *RemoteClient) Put(data []byte) error { } ctx := context.TODO() + + if c.snapshot { + snapshotInput := blobs.SnapshotInput{LeaseID: options.LeaseID} + + log.Printf("[DEBUG] Snapshotting existing Blob %q (Container %q / Account %q)", c.keyName, c.containerName, c.accountName) + if _, err := c.giovanniBlobClient.Snapshot(ctx, c.accountName, c.containerName, c.keyName, snapshotInput); err != nil { + return fmt.Errorf("error snapshotting Blob %q (Container %q / Account %q): %+v", c.keyName, c.containerName, c.accountName, err) + } + + log.Print("[DEBUG] Created blob snapshot") + } + blob, err := c.giovanniBlobClient.GetProperties(ctx, c.accountName, c.containerName, c.keyName, getOptions) if err != nil { if blob.StatusCode != 404 { diff --git a/website/docs/backends/types/azurerm.html.md b/website/docs/backends/types/azurerm.html.md index 02d57b2eee9a..05ceb2d9d581 100644 --- a/website/docs/backends/types/azurerm.html.md +++ b/website/docs/backends/types/azurerm.html.md @@ -154,6 +154,8 @@ The following configuration options are supported: * `environment` - (Optional) The Azure Environment which should be used. This can also be sourced from the `ARM_ENVIRONMENT` environment variable. Possible values are `public`, `china`, `german`, `stack` and `usgovernment`. Defaults to `public`. +* `snapshot` - (Optional) Should the Blob used to store the Terraform Statefile be snapshotted before use? Defaults to `false`. This value can also be sourced from the `ARM_SNAPSHOT` environment variable. + * `endpoint` - (Optional) The Custom Endpoint for Azure Resource Manager. This can also be sourced from the `ARM_ENDPOINT` environment variable. ~> **NOTE:** An `endpoint` should only be configured when using Azure Stack.