Skip to content

Commit

Permalink
Merge pull request #26 from hashicorp/main
Browse files Browse the repository at this point in the history
Fork Sync: Update from parent repository
  • Loading branch information
mbialon authored Sep 26, 2023
2 parents a665e26 + da74682 commit f255fe2
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ BUG FIXES:
* `azurerm_api_management_api` : fix importing `openapi` format content file issue [GH-23348]
* `azurerm_palo_alto_local_rulestack_prefix_list` - fix rulestack not being committed on delete [GH-23362]
* `azurerm_palo_alto_local_rulestack_fqdn_list` - fix rulestack not being committed on delete [GH-23362]
* `security_center_subscription_pricing_resource` - disabled extensions logic now works as expected [GH-22997]


## 3.74.0 (September 21, 2023)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,23 +134,53 @@ func resourceSecurityCenterSubscriptionPricingUpdate(d *pluginsdk.ResourceData,
}

extensionsStatusFromBackend := make([]pricings_v2023_01_01.Extension, 0)
if err == nil && apiResponse.Model != nil && apiResponse.Model.Properties != nil && apiResponse.Model.Properties.Extensions != nil {
extensionsStatusFromBackend = *apiResponse.Model.Properties.Extensions
isCurrentlyInFree := false
if err == nil && apiResponse.Model != nil && apiResponse.Model.Properties != nil {
if apiResponse.Model.Properties.Extensions != nil {
extensionsStatusFromBackend = *apiResponse.Model.Properties.Extensions
}

if apiResponse.Model.Properties.PricingTier == pricings_v2023_01_01.PricingTierFree {
isCurrentlyInFree = true
}
}

if vSub, okSub := d.GetOk("subplan"); okSub {
pricing.Properties.SubPlan = utils.String(vSub.(string))
}
if d.HasChange("extension") || d.IsNewResource() {
if d.HasChange("extension") {
// can not set extensions for free tier
if pricing.Properties.PricingTier == pricings_v2023_01_01.PricingTierStandard {
var extensions = expandSecurityCenterSubscriptionPricingExtensions(d.Get("extension").(*pluginsdk.Set).List(), &extensionsStatusFromBackend)
pricing.Properties.Extensions = extensions
}
}

if _, err := client.Update(ctx, id, pricing); err != nil {
return fmt.Errorf("setting %s: %+v", id, err)
if len(d.Get("extension").(*pluginsdk.Set).List()) > 0 && pricing.Properties.PricingTier == pricings_v2023_01_01.PricingTierFree {
return fmt.Errorf("extensions cannot be enabled when using free tier")
}

updateResponse, updateErr := client.Update(ctx, id, pricing)
if updateErr != nil {
return fmt.Errorf("setting %s: %+v", id, updateErr)
}

if updateErr == nil && updateResponse.Model != nil && updateResponse.Model.Properties != nil {
if updateResponse.Model.Properties.Extensions != nil {
extensionsStatusFromBackend = *updateResponse.Model.Properties.Extensions
}
}

// after turning on the bundle, we have now the extensions list
if d.IsNewResource() || isCurrentlyInFree {
var extensions = expandSecurityCenterSubscriptionPricingExtensions(d.Get("extension").(*pluginsdk.Set).List(), &extensionsStatusFromBackend)
pricing.Properties.Extensions = extensions
_, updateErr := client.Update(ctx, id, pricing)
if err != nil {
if updateErr != nil {
return fmt.Errorf("setting %s: %+v", id, updateErr)
}
}
}

d.SetId(id.ID())
Expand Down Expand Up @@ -218,32 +248,29 @@ func resourceSecurityCenterSubscriptionPricingDelete(d *pluginsdk.ResourceData,
}

func expandSecurityCenterSubscriptionPricingExtensions(inputList []interface{}, extensionsStatusFromBackend *[]pricings_v2023_01_01.Extension) *[]pricings_v2023_01_01.Extension {
if len(inputList) == 0 {
return nil
}
var extensionStatuses = map[string]bool{}
var extensionProperties = map[string]*interface{}{}

var outputList []pricings_v2023_01_01.Extension

if extensionsStatusFromBackend != nil {
for _, backendExtension := range *extensionsStatusFromBackend {
// set the default value to false, then turn on the extension that appear in the template
extensionStatuses[backendExtension.Name] = false
}
}

// set any extension in the template to be true
for _, v := range inputList {
input := v.(map[string]interface{})
if input["name"] == "" {
continue
}
extensionStatuses[input["name"].(string)] = true

if vAdditional, ok := input["additional_extension_properties"]; ok {
extensionProperties[input["name"].(string)] = &vAdditional
}
}

if extensionsStatusFromBackend != nil {
for _, backendExtension := range *extensionsStatusFromBackend {
_, ok := extensionStatuses[backendExtension.Name]
// set any extension that does not appear in the template to be false
if !ok {
extensionStatuses[backendExtension.Name] = false
}
}
}

for extensionName, toBeEnabled := range extensionStatuses {

isEnabled := pricings_v2023_01_01.IsEnabledFalse
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,61 @@ func TestAccSecurityCenterSubscriptionPricing_cloudPostureExtension(t *testing.T
})
}

func TestAccSecurityCenterSubscriptionPricing_cloudPostureExtensionFreeToStandardDisabledExtensions(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_security_center_subscription_pricing", "test")
r := SecurityCenterSubscriptionPricingResource{}

data.ResourceSequentialTestSkipCheckDestroyed(t, []acceptance.TestStep{
{
Config: r.cloudPostureFree(),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
check.That(data.ResourceName).Key("tier").HasValue("Free"),
check.That(data.ResourceName).Key("resource_type").HasValue("CloudPosture"),
),
},
data.ImportStep(),
{
Config: r.cloudPostureStandard(),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
check.That(data.ResourceName).Key("tier").HasValue("Standard"),
check.That(data.ResourceName).Key("resource_type").HasValue("CloudPosture"),
check.That(data.ResourceName).Key("extension.#").HasValue("0"),
),
},
data.ImportStep(),
})
}

func TestAccSecurityCenterSubscriptionPricing_cloudPostureExtensioStandardToFreeExtensions(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_security_center_subscription_pricing", "test")
r := SecurityCenterSubscriptionPricingResource{}

data.ResourceSequentialTestSkipCheckDestroyed(t, []acceptance.TestStep{
{
Config: r.cloudPostureExtension(),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
check.That(data.ResourceName).Key("tier").HasValue("Standard"),
check.That(data.ResourceName).Key("resource_type").HasValue("CloudPosture"),
check.That(data.ResourceName).Key("extension.#").HasValue("2"),
),
},
data.ImportStep(),
{
Config: r.cloudPostureFree(),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
check.That(data.ResourceName).Key("tier").HasValue("Free"),
check.That(data.ResourceName).Key("resource_type").HasValue("CloudPosture"),
check.That(data.ResourceName).Key("extension.#").HasValue("0"),
),
},
data.ImportStep(),
})
}

func (SecurityCenterSubscriptionPricingResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) {
id, err := pricings_v2023_01_01.ParsePricingIDInsensitively(state.ID)
if err != nil {
Expand Down Expand Up @@ -231,3 +286,33 @@ resource "azurerm_security_center_subscription_pricing" "test" {
}
`
}

func (SecurityCenterSubscriptionPricingResource) cloudPostureFree() string {
return `
provider "azurerm" {
features {
}
}
resource "azurerm_security_center_subscription_pricing" "test" {
tier = "Free"
resource_type = "CloudPosture"
}
`
}

func (SecurityCenterSubscriptionPricingResource) cloudPostureStandard() string {
return `
provider "azurerm" {
features {
}
}
resource "azurerm_security_center_subscription_pricing" "test" {
tier = "Standard"
resource_type = "CloudPosture"
}
`
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,43 @@ Manages the Pricing Tier for Azure Security Center in the current subscription.

## Example Usage

### Basic usage

```hcl
resource "azurerm_security_center_subscription_pricing" "example" {
tier = "Standard"
resource_type = "VirtualMachines"
}
```

### Using Extensions with Defender CSPM

```hcl
resource "azurerm_security_center_subscription_pricing" "example1" {
tier = "Standard"
resource_type = "CloudPosture"
extension {
name = "ContainerRegistriesVulnerabilityAssessments"
}
extension {
name = "AgentlessVmScanning"
additional_extension_properties = {
ExclusionTags = "[]"
}
}
extension {
name = "AgentlessDiscoveryForKubernetes"
}
extension {
name = "SensitiveDataDiscovery"
}
}
```

## Argument Reference

The following arguments are supported:
Expand All @@ -38,7 +68,7 @@ A `extension` block supports the following:

* `additional_extension_properties` - (Optional) Key/Value pairs that are required for some extensions.

~> **NOTE:** If an extension is not defined, it will not be enabled. Use `ignore_changes` on the `extension` field if you want to use the default extensions.
~> **NOTE:** If an extension is not defined, it will not be enabled.

~> **NOTE:** Changing the pricing tier to `Standard` affects all resources of the given type in the subscription and could be quite costly.

Expand Down

0 comments on commit f255fe2

Please sign in to comment.