-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New Resource:
azurerm_storage_table_entity
(#3831)
- Loading branch information
Showing
22 changed files
with
1,544 additions
and
4 deletions.
There are no files selected for viewing
87 changes: 87 additions & 0 deletions
87
azurerm/internal/authorizers/authorizer_shared_key_lite_table.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package authorizers | ||
|
||
import ( | ||
"net/http" | ||
"strings" | ||
|
||
"github.com/Azure/go-autorest/autorest" | ||
) | ||
|
||
// TODO: switch to using the version from github.com/Azure/go-autorest | ||
// once https://github.com/Azure/go-autorest/pull/416 has been merged | ||
|
||
// SharedKeyLiteTableAuthorizer implements an authorization for Shared Key Lite | ||
// this can be used for interaction with Table Storage Endpoints | ||
type SharedKeyLiteTableAuthorizer struct { | ||
storageAccountName string | ||
storageAccountKey string | ||
} | ||
|
||
// NewSharedKeyLiteAuthorizer crates a SharedKeyLiteAuthorizer using the given credentials | ||
func NewSharedKeyLiteTableAuthorizer(accountName, accountKey string) *SharedKeyLiteTableAuthorizer { | ||
return &SharedKeyLiteTableAuthorizer{ | ||
storageAccountName: accountName, | ||
storageAccountKey: accountKey, | ||
} | ||
} | ||
|
||
// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose | ||
// value is "SharedKeyLite " followed by the computed key. | ||
// This can be used for the Blob, Queue, and File Services | ||
// | ||
// from: https://docs.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key | ||
// You may use Shared Key Lite authorization to authorize a request made against the | ||
// 2009-09-19 version and later of the Blob and Queue services, | ||
// and version 2014-02-14 and later of the File services. | ||
func (skl *SharedKeyLiteTableAuthorizer) WithAuthorization() autorest.PrepareDecorator { | ||
return func(p autorest.Preparer) autorest.Preparer { | ||
return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) { | ||
r, err := p.Prepare(r) | ||
if err != nil { | ||
return r, err | ||
} | ||
|
||
key, err := buildSharedKeyLiteTable(skl.storageAccountName, skl.storageAccountKey, r) | ||
if err != nil { | ||
return r, err | ||
} | ||
|
||
sharedKeyHeader := formatSharedKeyLiteAuthorizationHeader(skl.storageAccountName, *key) | ||
return autorest.Prepare(r, autorest.WithHeader(HeaderAuthorization, sharedKeyHeader)) | ||
}) | ||
} | ||
} | ||
|
||
func buildSharedKeyLiteTable(accountName, storageAccountKey string, r *http.Request) (*string, error) { | ||
// first ensure the relevant headers are configured | ||
prepareHeadersForRequest(r) | ||
|
||
sharedKey, err := computeSharedKeyLiteTable(r.URL.String(), accountName, r.Header) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// we then need to HMAC that value | ||
hmacdValue := hmacValue(storageAccountKey, *sharedKey) | ||
return &hmacdValue, nil | ||
} | ||
|
||
// computeSharedKeyLite computes the Shared Key Lite required for Storage Authentication | ||
// NOTE: this function assumes that the `x-ms-date` field is set | ||
func computeSharedKeyLiteTable(url string, accountName string, headers http.Header) (*string, error) { | ||
dateHeader := headers.Get("x-ms-date") | ||
canonicalizedResource, err := buildCanonicalizedResource(url, accountName) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
canonicalizedString := buildCanonicalizedStringForSharedKeyLiteTable(*canonicalizedResource, dateHeader) | ||
return &canonicalizedString, nil | ||
} | ||
|
||
func buildCanonicalizedStringForSharedKeyLiteTable(canonicalizedResource, dateHeader string) string { | ||
return strings.Join([]string{ | ||
dateHeader, | ||
canonicalizedResource, | ||
}, "\n") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
package azurerm | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
|
||
"github.com/hashicorp/terraform/helper/schema" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" | ||
"github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/entities" | ||
) | ||
|
||
func resourceArmStorageTableEntity() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceArmStorageTableEntityCreateUpdate, | ||
Read: resourceArmStorageTableEntityRead, | ||
Update: resourceArmStorageTableEntityCreateUpdate, | ||
Delete: resourceArmStorageTableEntityDelete, | ||
Importer: &schema.ResourceImporter{ | ||
State: schema.ImportStatePassthrough, | ||
}, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"table_name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validateArmStorageTableName, | ||
}, | ||
"storage_account_name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validate.NoEmptyStrings, | ||
}, | ||
"partition_key": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validate.NoEmptyStrings, | ||
}, | ||
"row_key": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validate.NoEmptyStrings, | ||
}, | ||
"entity": { | ||
Type: schema.TypeMap, | ||
Required: true, | ||
Elem: &schema.Schema{ | ||
Type: schema.TypeString, | ||
}, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceArmStorageTableEntityCreateUpdate(d *schema.ResourceData, meta interface{}) error { | ||
ctx := meta.(*ArmClient).StopContext | ||
storageClient := meta.(*ArmClient).storage | ||
|
||
accountName := d.Get("storage_account_name").(string) | ||
tableName := d.Get("table_name").(string) | ||
partitionKey := d.Get("partition_key").(string) | ||
rowKey := d.Get("row_key").(string) | ||
entity := d.Get("entity").(map[string]interface{}) | ||
|
||
resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName) | ||
if err != nil { | ||
return fmt.Errorf("Error locating Resource Group: %s", err) | ||
} | ||
|
||
client, err := storageClient.TableEntityClient(ctx, *resourceGroup, accountName) | ||
if err != nil { | ||
return fmt.Errorf("Error building Entity Client: %s", err) | ||
} | ||
|
||
if requireResourcesToBeImported { | ||
input := entities.GetEntityInput{ | ||
PartitionKey: partitionKey, | ||
RowKey: rowKey, | ||
MetaDataLevel: entities.NoMetaData, | ||
} | ||
existing, err := client.Get(ctx, accountName, tableName, input) | ||
if err != nil { | ||
if !utils.ResponseWasNotFound(existing.Response) { | ||
return fmt.Errorf("Error checking for presence of existing Entity (Partition Key %q / Row Key %q) (Table %q / Storage Account %q / Resource Group %q): %s", partitionKey, rowKey, tableName, accountName, *resourceGroup, err) | ||
} | ||
} | ||
|
||
if !utils.ResponseWasNotFound(existing.Response) { | ||
id := client.GetResourceID(accountName, tableName, partitionKey, rowKey) | ||
return tf.ImportAsExistsError("azurerm_storage_table_entity", id) | ||
} | ||
} | ||
|
||
input := entities.InsertOrMergeEntityInput{ | ||
PartitionKey: partitionKey, | ||
RowKey: rowKey, | ||
Entity: entity, | ||
} | ||
|
||
if _, err := client.InsertOrMerge(ctx, accountName, tableName, input); err != nil { | ||
return fmt.Errorf("Error creating Entity (Partition Key %q / Row Key %q) (Table %q / Storage Account %q / Resource Group %q): %+v", partitionKey, rowKey, tableName, accountName, *resourceGroup, err) | ||
} | ||
|
||
resourceID := client.GetResourceID(accountName, tableName, partitionKey, rowKey) | ||
d.SetId(resourceID) | ||
|
||
return resourceArmStorageTableEntityRead(d, meta) | ||
} | ||
|
||
func resourceArmStorageTableEntityRead(d *schema.ResourceData, meta interface{}) error { | ||
ctx := meta.(*ArmClient).StopContext | ||
storageClient := meta.(*ArmClient).storage | ||
|
||
id, err := entities.ParseResourceID(d.Id()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
resourceGroup, err := storageClient.FindResourceGroup(ctx, id.AccountName) | ||
if err != nil { | ||
return fmt.Errorf("Error locating Resource Group for Storage Account %q: %s", id.AccountName, err) | ||
} | ||
if resourceGroup == nil { | ||
log.Printf("[DEBUG] Unable to locate Resource Group for Storage Account %q - assuming removed & removing from state", id.AccountName) | ||
d.SetId("") | ||
return nil | ||
} | ||
|
||
client, err := storageClient.TableEntityClient(ctx, *resourceGroup, id.AccountName) | ||
if err != nil { | ||
return fmt.Errorf("Error building Table Entity Client for Storage Account %q (Resource Group %q): %s", id.AccountName, *resourceGroup, err) | ||
} | ||
|
||
input := entities.GetEntityInput{ | ||
PartitionKey: id.PartitionKey, | ||
RowKey: id.RowKey, | ||
MetaDataLevel: entities.NoMetaData, | ||
} | ||
|
||
result, err := client.Get(ctx, id.AccountName, id.TableName, input) | ||
if err != nil { | ||
return fmt.Errorf("Error retrieving Entity (Partition Key %q / Row Key %q) (Table %q / Storage Account %q / Resource Group %q): %s", id.PartitionKey, id.RowKey, id.TableName, id.AccountName, *resourceGroup, err) | ||
} | ||
|
||
d.Set("storage_account_name", id.AccountName) | ||
d.Set("table_name", id.TableName) | ||
d.Set("partition_key", id.PartitionKey) | ||
d.Set("row_key", id.RowKey) | ||
if err := d.Set("entity", flattenEntity(result.Entity)); err != nil { | ||
return fmt.Errorf("Error setting `entity` for Entity (Partition Key %q / Row Key %q) (Table %q / Storage Account %q / Resource Group %q): %s", id.PartitionKey, id.RowKey, id.TableName, id.AccountName, *resourceGroup, err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func resourceArmStorageTableEntityDelete(d *schema.ResourceData, meta interface{}) error { | ||
ctx := meta.(*ArmClient).StopContext | ||
storageClient := meta.(*ArmClient).storage | ||
|
||
id, err := entities.ParseResourceID(d.Id()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
resourceGroup, err := storageClient.FindResourceGroup(ctx, id.AccountName) | ||
if err != nil { | ||
return fmt.Errorf("Error locating Resource Group for Storage Account %q: %s", id.AccountName, err) | ||
} | ||
if resourceGroup == nil { | ||
log.Printf("[DEBUG] Unable to locate Resource Group for Storage Account %q - assuming removed already", id.AccountName) | ||
d.SetId("") | ||
return nil | ||
} | ||
|
||
client, err := storageClient.TableEntityClient(ctx, *resourceGroup, id.AccountName) | ||
if err != nil { | ||
return fmt.Errorf("Error building Entity Client for Storage Account %q (Resource Group %q): %s", id.AccountName, *resourceGroup, err) | ||
} | ||
|
||
input := entities.DeleteEntityInput{ | ||
PartitionKey: id.PartitionKey, | ||
RowKey: id.RowKey, | ||
} | ||
|
||
if _, err := client.Delete(ctx, id.AccountName, id.TableName, input); err != nil { | ||
return fmt.Errorf("Error deleting Entity (Partition Key %q / Row Key %q) (Table %q / Storage Account %q / Resource Group %q): %s", id.PartitionKey, id.RowKey, id.TableName, id.AccountName, *resourceGroup, err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// The api returns extra information that we already have. We'll remove it here before setting it in state. | ||
func flattenEntity(entity map[string]interface{}) map[string]interface{} { | ||
delete(entity, "PartitionKey") | ||
delete(entity, "RowKey") | ||
delete(entity, "Timestamp") | ||
|
||
return entity | ||
} |
Oops, something went wrong.