Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

azurerm_storage_container and azurerm_storage_blob: support for encryption scope #25551

Merged
merged 5 commits into from Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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