Skip to content

Commit

Permalink
Merge pull request #25551 from hashicorp/f/storage-blob-container-enc…
Browse files Browse the repository at this point in the history
…ryption-scope

`azurerm_storage_container` and `azurerm_storage_blob`: support for encryption scope
  • Loading branch information
manicminer committed Apr 10, 2024
2 parents dde59d1 + 4d1d622 commit 00d040e
Show file tree
Hide file tree
Showing 24 changed files with 468 additions and 51 deletions.
2 changes: 1 addition & 1 deletion go.mod
Expand Up @@ -29,7 +29,7 @@ require (
github.com/mitchellh/mapstructure v1.5.0
github.com/rickb777/date v1.12.5-0.20200422084442-6300e543c4d9
github.com/sergi/go-diff v1.2.0
github.com/tombuildsstuff/giovanni v0.25.5
github.com/tombuildsstuff/giovanni v0.26.1
github.com/tombuildsstuff/kermit v0.20240122.1123108
golang.org/x/crypto v0.18.0
golang.org/x/tools v0.13.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Expand Up @@ -225,8 +225,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tombuildsstuff/giovanni v0.25.5 h1:YjVuiurzRAZDwTsWtelYOJV3373fMHXEazirh6h7nhU=
github.com/tombuildsstuff/giovanni v0.25.5/go.mod h1:s7xbU2lN5Iz9MBglmDDv9p2QPbn6x3UkJBtpCfUerLs=
github.com/tombuildsstuff/giovanni v0.26.1 h1:RZgnpyIHtgw0GXYpw3xttNk35obJNoI1hztCZsh/Djo=
github.com/tombuildsstuff/giovanni v0.26.1/go.mod h1:s7xbU2lN5Iz9MBglmDDv9p2QPbn6x3UkJBtpCfUerLs=
github.com/tombuildsstuff/kermit v0.20240122.1123108 h1:icQaxsv/ANv/KC4Sr0V1trrWA/XIL+3QAVBDpiSTgj8=
github.com/tombuildsstuff/kermit v0.20240122.1123108/go.mod h1:T3YBVFhRV4qA7SbnRaNE6eapIMpKDA9rG/V7Ocsjlno=
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
Expand Down
53 changes: 36 additions & 17 deletions internal/services/storage/blobs.go
Expand Up @@ -15,7 +15,7 @@ import (
"strings"
"sync"

"github.com/hashicorp/terraform-provider-azurerm/utils"
"github.com/hashicorp/go-azure-helpers/lang/pointer"
"github.com/tombuildsstuff/giovanni/storage/2023-11-03/blob/blobs"
)

Expand All @@ -26,16 +26,17 @@ type BlobUpload struct {
BlobName string
ContainerName string

BlobType string
CacheControl string
ContentType string
ContentMD5 string
MetaData map[string]string
Parallelism int
Size int
Source string
SourceContent string
SourceUri string
BlobType string
CacheControl string
ContentType string
ContentMD5 string
EncryptionScope string
MetaData map[string]string
Parallelism int
Size int
Source string
SourceContent string
SourceUri string
}

func (sbu BlobUpload) Create(ctx context.Context) error {
Expand Down Expand Up @@ -93,6 +94,9 @@ func (sbu BlobUpload) copy(ctx context.Context) error {
CopySource: sbu.SourceUri,
MetaData: sbu.MetaData,
}
if sbu.EncryptionScope != "" {
input.EncryptionScope = pointer.To(sbu.EncryptionScope)
}
if err := sbu.Client.CopyAndWait(ctx, sbu.ContainerName, sbu.BlobName, input); err != nil {
return fmt.Errorf("copy/waiting: %s", err)
}
Expand All @@ -102,9 +106,12 @@ func (sbu BlobUpload) copy(ctx context.Context) error {

func (sbu BlobUpload) createEmptyAppendBlob(ctx context.Context) error {
input := blobs.PutAppendBlobInput{
ContentType: utils.String(sbu.ContentType),
ContentType: pointer.To(sbu.ContentType),
MetaData: sbu.MetaData,
}
if sbu.EncryptionScope != "" {
input.EncryptionScope = pointer.To(sbu.EncryptionScope)
}
if _, err := sbu.Client.PutAppendBlob(ctx, sbu.ContainerName, sbu.BlobName, input); err != nil {
return fmt.Errorf("PutAppendBlob: %s", err)
}
Expand All @@ -118,9 +125,12 @@ func (sbu BlobUpload) createEmptyBlockBlob(ctx context.Context) error {
}

input := blobs.PutBlockBlobInput{
ContentType: utils.String(sbu.ContentType),
ContentType: pointer.To(sbu.ContentType),
MetaData: sbu.MetaData,
}
if sbu.EncryptionScope != "" {
input.EncryptionScope = pointer.To(sbu.EncryptionScope)
}
if _, err := sbu.Client.PutBlockBlob(ctx, sbu.ContainerName, sbu.BlobName, input); err != nil {
return fmt.Errorf("PutBlockBlob: %s", err)
}
Expand Down Expand Up @@ -152,11 +162,14 @@ func (sbu BlobUpload) uploadBlockBlob(ctx context.Context) error {
defer file.Close()

input := blobs.PutBlockBlobInput{
ContentType: utils.String(sbu.ContentType),
ContentType: pointer.To(sbu.ContentType),
MetaData: sbu.MetaData,
}
if sbu.ContentMD5 != "" {
input.ContentMD5 = utils.String(sbu.ContentMD5)
input.ContentMD5 = pointer.To(sbu.ContentMD5)
}
if sbu.EncryptionScope != "" {
input.EncryptionScope = pointer.To(sbu.EncryptionScope)
}
if err := sbu.Client.PutBlockBlobFromFile(ctx, sbu.ContainerName, sbu.BlobName, file, input); err != nil {
return fmt.Errorf("PutBlockBlobFromFile: %s", err)
Expand All @@ -172,9 +185,12 @@ func (sbu BlobUpload) createEmptyPageBlob(ctx context.Context) error {

input := blobs.PutPageBlobInput{
BlobContentLengthBytes: int64(sbu.Size),
ContentType: utils.String(sbu.ContentType),
ContentType: pointer.To(sbu.ContentType),
MetaData: sbu.MetaData,
}
if sbu.EncryptionScope != "" {
input.EncryptionScope = pointer.To(sbu.EncryptionScope)
}
if _, err := sbu.Client.PutPageBlob(ctx, sbu.ContainerName, sbu.BlobName, input); err != nil {
return fmt.Errorf("PutPageBlob: %s", err)
}
Expand Down Expand Up @@ -222,9 +238,12 @@ func (sbu BlobUpload) uploadPageBlob(ctx context.Context) error {
// first let's create a file of the specified file size
input := blobs.PutPageBlobInput{
BlobContentLengthBytes: fileSize,
ContentType: utils.String(sbu.ContentType),
ContentType: pointer.To(sbu.ContentType),
MetaData: sbu.MetaData,
}
if sbu.EncryptionScope != "" {
input.EncryptionScope = pointer.To(sbu.EncryptionScope)
}
if _, err := sbu.Client.PutPageBlob(ctx, sbu.ContainerName, sbu.BlobName, input); err != nil {
return fmt.Errorf("PutPageBlob: %s", err)
}
Expand Down
10 changes: 6 additions & 4 deletions internal/services/storage/shim/containers.go
Expand Up @@ -19,8 +19,10 @@ type StorageContainerWrapper interface {
}

type StorageContainerProperties struct {
AccessLevel containers.AccessLevel
MetaData map[string]string
HasImmutabilityPolicy bool
HasLegalHold bool
AccessLevel containers.AccessLevel
DefaultEncryptionScope string
EncryptionScopeOverrideDisabled bool
MetaData map[string]string
HasImmutabilityPolicy bool
HasLegalHold bool
}
10 changes: 6 additions & 4 deletions internal/services/storage/shim/containers_data_plane.go
Expand Up @@ -60,10 +60,12 @@ func (w DataPlaneStorageContainerWrapper) Get(ctx context.Context, containerName
}

return &StorageContainerProperties{
AccessLevel: props.AccessLevel,
MetaData: props.MetaData,
HasImmutabilityPolicy: props.HasImmutabilityPolicy,
HasLegalHold: props.HasLegalHold,
AccessLevel: props.AccessLevel,
DefaultEncryptionScope: props.DefaultEncryptionScope,
EncryptionScopeOverrideDisabled: props.EncryptionScopeOverrideDisabled,
MetaData: props.MetaData,
HasImmutabilityPolicy: props.HasImmutabilityPolicy,
HasLegalHold: props.HasLegalHold,
}, nil
}

Expand Down
7 changes: 7 additions & 0 deletions internal/services/storage/storage_blob_data_source.go
Expand Up @@ -63,6 +63,11 @@ func dataSourceStorageBlob() *pluginsdk.Resource {
Computed: true,
},

"encryption_scope": {
Type: pluginsdk.TypeString,
Computed: true,
},

"url": {
Type: pluginsdk.TypeString,
Computed: true,
Expand Down Expand Up @@ -136,6 +141,8 @@ func dataSourceStorageBlobRead(d *pluginsdk.ResourceData, meta interface{}) erro
}
d.Set("content_md5", contentMD5)

d.Set("encryption_scope", props.EncryptionScope)

d.Set("type", strings.TrimSuffix(string(props.BlobType), "Blob"))

d.SetId(id.ID())
Expand Down
20 changes: 14 additions & 6 deletions internal/services/storage/storage_blob_data_source_test.go
Expand Up @@ -30,6 +30,7 @@ func TestAccDataSourceStorageBlob_basic(t *testing.T) {
{
Config: StorageBlobDataSource{}.basicWithDataSource(data, sourceBlob.Name()),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).Key("encryption_scope").HasValue(fmt.Sprintf("acctestEScontainer%d", data.RandomInteger)),
check.That(data.ResourceName).Key("type").HasValue("Block"),
check.That(data.ResourceName).Key("metadata.%").HasValue("2"),
check.That(data.ResourceName).Key("metadata.k1").HasValue("v1"),
Expand All @@ -46,21 +47,27 @@ provider "azurerm" {
}
resource "azurerm_resource_group" "test" {
name = "blobdstest-%s"
location = "%s"
name = "blobdstest-%[1]s"
location = "%[2]s"
}
resource "azurerm_storage_account" "test" {
name = "acctestsadsc%s"
name = "acctestsadsc%[1]s"
resource_group_name = "${azurerm_resource_group.test.name}"
location = "${azurerm_resource_group.test.location}"
account_tier = "Standard"
account_replication_type = "LRS"
}
resource "azurerm_storage_encryption_scope" "test" {
name = "acctestEScontainer%[3]d"
storage_account_id = azurerm_storage_account.test.id
source = "Microsoft.Storage"
}
resource "azurerm_storage_container" "test" {
name = "containerdstest-%s"
name = "containerdstest-%[1]s"
storage_account_name = "${azurerm_storage_account.test.name}"
container_access_type = "private"
}
Expand All @@ -69,15 +76,16 @@ resource "azurerm_storage_blob" "test" {
name = "example.vhd"
storage_account_name = azurerm_storage_account.test.name
storage_container_name = azurerm_storage_container.test.name
encryption_scope = azurerm_storage_encryption_scope.test.name
type = "Block"
source = "%s"
source = "%[4]s"
metadata = {
k1 = "v1"
k2 = "v2"
}
}
`, data.RandomString, data.Locations.Primary, data.RandomString, data.RandomString, fileName)
`, data.RandomString, data.Locations.Primary, data.RandomInteger, fileName)
}

func (d StorageBlobDataSource) basicWithDataSource(data acceptance.TestData, fileName string) string {
Expand Down
41 changes: 35 additions & 6 deletions internal/services/storage/storage_blob_resource.go
Expand Up @@ -10,6 +10,7 @@ import (
"strings"
"time"

"github.com/hashicorp/go-azure-helpers/lang/pointer"
"github.com/hashicorp/go-azure-helpers/lang/response"
"github.com/hashicorp/terraform-provider-azurerm/helpers/tf"
"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
Expand All @@ -19,7 +20,6 @@ import (
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation"
"github.com/hashicorp/terraform-provider-azurerm/internal/timeouts"
"github.com/hashicorp/terraform-provider-azurerm/utils"
"github.com/tombuildsstuff/giovanni/storage/2023-11-03/blob/accounts"
"github.com/tombuildsstuff/giovanni/storage/2023-11-03/blob/blobs"
)
Expand Down Expand Up @@ -111,6 +111,13 @@ func resourceStorageBlob() *pluginsdk.Resource {
Optional: true,
},

"encryption_scope": {
Type: pluginsdk.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validate.StorageEncryptionScopeName,
},

"source": {
Type: pluginsdk.TypeString,
Optional: true,
Expand Down Expand Up @@ -239,6 +246,11 @@ func resourceStorageBlobCreate(d *pluginsdk.ResourceData, meta interface{}) erro
SourceContent: d.Get("source_content").(string),
SourceUri: d.Get("source_uri").(string),
}

if encryptionScope := d.Get("encryption_scope"); encryptionScope.(string) != "" {
blobInput.EncryptionScope = encryptionScope.(string)
}

if err = blobInput.Create(ctx); err != nil {
return fmt.Errorf("creating %s: %v", id, err)
}
Expand Down Expand Up @@ -273,11 +285,24 @@ func resourceStorageBlobUpdate(d *pluginsdk.ResourceData, meta interface{}) erro
return fmt.Errorf("building Blobs Client: %v", err)
}

log.Printf("[INFO] Retrieving %s", id)
input := blobs.GetPropertiesInput{}
props, err := blobsClient.GetProperties(ctx, id.ContainerName, id.BlobName, input)
if err != nil {
if response.WasNotFound(props.HttpResponse) {
log.Printf("[INFO] Blob %q was not found in Container %q / Account %q - assuming removed & removing from state...", id.BlobName, id.ContainerName, id.AccountId.AccountName)
d.SetId("")
return nil
}

return fmt.Errorf("retrieving properties for %s: %v", id, err)
}

if d.HasChange("content_type") || d.HasChange("cache_control") {
log.Printf("[DEBUG] Updating Properties for %s...", id)
input := blobs.SetPropertiesInput{
ContentType: utils.String(d.Get("content_type").(string)),
CacheControl: utils.String(d.Get("cache_control").(string)),
ContentType: pointer.To(d.Get("content_type").(string)),
CacheControl: pointer.To(d.Get("cache_control").(string)),
}

// `content_md5` is `ForceNew` but must be included in the `SetPropertiesInput` update payload, or it will be zeroed on the blob.
Expand All @@ -286,10 +311,8 @@ func resourceStorageBlobUpdate(d *pluginsdk.ResourceData, meta interface{}) erro
if err != nil {
return fmt.Errorf("converting hex to base64 encoding for content_md5: %v", err)
}

input.ContentMD5 = utils.String(data)
input.ContentMD5 = pointer.To(data)
}

if _, err = blobsClient.SetProperties(ctx, id.ContainerName, id.BlobName, input); err != nil {
return fmt.Errorf("updating Properties for %s: %v", id, err)
}
Expand All @@ -302,6 +325,10 @@ func resourceStorageBlobUpdate(d *pluginsdk.ResourceData, meta interface{}) erro
input := blobs.SetMetaDataInput{
MetaData: ExpandMetaData(metaDataRaw),
}
// Encryption Scope must be specified when updating metadata
if props.EncryptionScope != "" {
input.EncryptionScope = pointer.To(props.EncryptionScope)
}
if _, err = blobsClient.SetMetaData(ctx, id.ContainerName, id.BlobName, input); err != nil {
return fmt.Errorf("updating MetaData for %s: %v", id, err)
}
Expand Down Expand Up @@ -380,6 +407,8 @@ func resourceStorageBlobRead(d *pluginsdk.ResourceData, meta interface{}) error
}
d.Set("content_md5", contentMD5)

d.Set("encryption_scope", props.EncryptionScope)

d.Set("type", strings.TrimSuffix(string(props.BlobType), "Blob"))
d.Set("url", d.Id())

Expand Down

0 comments on commit 00d040e

Please sign in to comment.