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

security_center_subscription_pricing_resource: refactor disabled extensions logic #22997

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
Loading