Skip to content

Commit

Permalink
Merge pull request #111 from Scalr/feature/SCALRSCORE-20549
Browse files Browse the repository at this point in the history
SCALRCORE-20549 > IP Fencing (IP whitelisting) - Provider
  • Loading branch information
emocharnik committed Mar 31, 2022
2 parents 0810da8 + 6cdb9ae commit 55a4237
Show file tree
Hide file tree
Showing 7 changed files with 297 additions and 3 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Expand Up @@ -9,15 +9,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- **New resource:** `scalr_account_allowed_ips` ([#111](https://github.com/Scalr/terraform-provider-scalr/pull/111))
- `scalr_workspace`: added new attribute `run_operation_timeout`

### Changed

- `resource.scalr_role`: added new state migration (include `accounts:set-quotas` permission if needed) ([#116](https://github.com/Scalr/terraform-provider-scalr/pull/108))

### Fixed

- Correctly handle not found resources ([#117](https://github.com/Scalr/terraform-provider-scalr/pull/117))

### Required

- scalr-server >= `8.15.0`

## [1.0.0-rc27] - 2022-02-17

### Fixed
Expand Down
40 changes: 40 additions & 0 deletions docs/resources/scalr_account_allowed_ips.md
@@ -0,0 +1,40 @@
---
layout: "scalr"
page_title: "Scalr: scalr_account_allowed_ips"
sidebar_current: "docs-resource-scalr-account-allowed-ips"
description: |-
Manages allowed ips for account.
---

# scalr_account_allowed_ips Resource

Manage the state of allowed ips for an account in Scalr. Create, update and destroy.

## Example Usage

Basic usage:

```hcl
resource "scalr_account_allowed_ips" "default" {
account_id = "acc-xxxxxxxx"
allowed_ips = ["127.0.0.1", "192.168.0.0/24"]
}
```

## Argument Reference

* `account_id` - (Required) ID of the account.
* `allowed_ips` - (Required) The list of allowed IPs or CIDRs.

## Attribute Reference

All arguments plus:

* `id` - The ID of the account.

## Import

To import allowed ips for an account use account ID as the import ID. For example:
```shell
terraform import scalr_account_allowed_ips.default acc-xxxxxxxxx
```
2 changes: 1 addition & 1 deletion go.mod
Expand Up @@ -5,7 +5,7 @@ require (
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce
github.com/hashicorp/terraform-plugin-sdk v1.17.2
github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734
github.com/scalr/go-scalr v0.0.0-20220325112237-ed0177c822fe
github.com/scalr/go-scalr v0.0.0-20220331085920-4ec72236c661
)

require (
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Expand Up @@ -299,8 +299,8 @@ github.com/posener/complete v1.2.1 h1:LrvDIY//XNo65Lq84G/akBuMGlawHvGBABv8f/ZN6D
github.com/posener/complete v1.2.1/go.mod h1:6gapUrK/U1TAN7ciCoNRIdVC5sbdBTUh1DKN0g6uH7E=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/scalr/go-scalr v0.0.0-20220325112237-ed0177c822fe h1:ByXm55tBvaFoLqUcwXd3O3VkpRG4BP2RYdtVyL/NUBw=
github.com/scalr/go-scalr v0.0.0-20220325112237-ed0177c822fe/go.mod h1:xMnwfer9UxugeNITZjTpQBwQ/4bw6/JdyDLpGdmyorE=
github.com/scalr/go-scalr v0.0.0-20220331085920-4ec72236c661 h1:DnvLMEoIGdre8txCto7ml8mvI/LcIA+uqtdhXRDUY3o=
github.com/scalr/go-scalr v0.0.0-20220331085920-4ec72236c661/go.mod h1:xMnwfer9UxugeNITZjTpQBwQ/4bw6/JdyDLpGdmyorE=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
Expand Down
1 change: 1 addition & 0 deletions scalr/provider.go
Expand Up @@ -81,6 +81,7 @@ func Provider() terraform.ResourceProvider {

ResourcesMap: map[string]*schema.Resource{
"scalr_access_policy": resourceScalrAccessPolicy(),
"scalr_account_allowed_ips": resourceScalrAccountAllowedIps(),
"scalr_agent_pool": resourceScalrAgentPool(),
"scalr_agent_pool_token": resourceScalrAgentPoolToken(),
"scalr_endpoint": resourceScalrEndpoint(),
Expand Down
138 changes: 138 additions & 0 deletions scalr/resource_scalr_account_allowed_ips.go
@@ -0,0 +1,138 @@
package scalr

import (
"errors"
"fmt"
"log"
"strings"

"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
scalr "github.com/scalr/go-scalr"
)

func resourceScalrAccountAllowedIps() *schema.Resource {
return &schema.Resource{
Create: resourceScalrAccountAllowedIpsCreate,
Read: resourceScalrAccountAllowedIpsRead,
Update: resourceScalrAccountAllowedIpsUpdate,
Delete: resourceScalrAccountAllowedIpsDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"account_id": {
Type: schema.TypeString,
Required: true,
},

"allowed_ips": {
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
MinItems: 1,
Required: true,
},
},
}
}

func preprocessAllowedIps(allowedIps []interface{}) []string {
ips := make([]string, 0)
for _, v := range allowedIps {
ips = append(ips, v.(string))
}

return ips
}

func resourceScalrAccountAllowedIpsCreate(d *schema.ResourceData, meta interface{}) error {
scalrClient := meta.(*scalr.Client)

// Get attributes.
accountId := d.Get("account_id").(string)

allowedIps := preprocessAllowedIps(d.Get("allowed_ips").([]interface{}))
// Create a new options struct.
options := scalr.AccountUpdateOptions{
ID: accountId,
AllowedIPs: &allowedIps,
}

log.Printf("[DEBUG] Update allowed ips: %s", accountId)
account, err := scalrClient.Accounts.Update(ctx, accountId, options)
if err != nil {
return fmt.Errorf("Error updating allowed ips for account %s: %v", accountId, err)
}

d.SetId(account.ID)

return resourceScalrAccountAllowedIpsRead(d, meta)
}

func resourceScalrAccountAllowedIpsRead(d *schema.ResourceData, meta interface{}) error {
scalrClient := meta.(*scalr.Client)

// Get the ID
accountID := d.Id()

log.Printf("[DEBUG] Read endpoint with ID: %s", accountID)
account, err := scalrClient.Accounts.Read(ctx, accountID)
if err != nil {
if errors.Is(err, scalr.ErrResourceNotFound) {
return fmt.Errorf("Could not find account %s: %v", accountID, err)
}
return fmt.Errorf("Error retrieving account: %v", err)
}

for i, ip := range account.AllowedIPs {
account.AllowedIPs[i] = strings.TrimSuffix(ip, "/32")
}

// Update the config.
d.Set("allowed_ips", account.AllowedIPs)
d.Set("account_id", accountID)

return nil
}

func resourceScalrAccountAllowedIpsUpdate(d *schema.ResourceData, meta interface{}) error {
scalrClient := meta.(*scalr.Client)

// Get attributes.
allowedIps := preprocessAllowedIps(d.Get("allowed_ips").([]interface{}))

// Create a new options struct.
options := scalr.AccountUpdateOptions{
ID: d.Id(),
AllowedIPs: &allowedIps,
}

log.Printf("[DEBUG] Update allowed ips for account: %s", d.Id())
_, err := scalrClient.Accounts.Update(ctx, d.Id(), options)
if err != nil {
return fmt.Errorf("Error updating allowed ips for %s: %v", d.Id(), err)
}

return resourceScalrAccountAllowedIpsRead(d, meta)
}

func resourceScalrAccountAllowedIpsDelete(d *schema.ResourceData, meta interface{}) error {
scalrClient := meta.(*scalr.Client)

log.Printf("[DEBUG] Delete allowed ips for account: %s", d.Id())

// Create a new options struct.
options := scalr.AccountUpdateOptions{
ID: d.Id(),
AllowedIPs: &[]string{},
}
_, err := scalrClient.Accounts.Update(ctx, d.Id(), options)
if err != nil {
if errors.Is(err, scalr.ErrResourceNotFound) {
return nil
}
return fmt.Errorf("Error deleting allowed ips for account %s: %v", d.Id(), err)
}

return nil
}
109 changes: 109 additions & 0 deletions scalr/resource_scalr_account_allowed_ips_test.go
@@ -0,0 +1,109 @@
package scalr

import (
"encoding/json"
"fmt"
"regexp"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
)

func TestAccScalrAccountAllowedIps_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccScalrAccountAllowedIps([]string{"192.168.0.12", "0.0.0.0/0"}),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet("scalr_account_allowed_ips.test", "id"),
resource.TestCheckResourceAttr("scalr_account_allowed_ips.test", "allowed_ips.#", "2"),
resource.TestCheckResourceAttr("scalr_account_allowed_ips.test", "allowed_ips.0", "192.168.0.12"),
resource.TestCheckResourceAttr("scalr_account_allowed_ips.test", "allowed_ips.1", "0.0.0.0/0"),
),
},
},
})
}

func TestAccScalrAccountAllowedIps_update(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccScalrAccountAllowedIps([]string{"192.168.0.12", "0.0.0.0/0"}),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet("scalr_account_allowed_ips.test", "id"),
resource.TestCheckResourceAttr("scalr_account_allowed_ips.test", "allowed_ips.#", "2"),
resource.TestCheckResourceAttr("scalr_account_allowed_ips.test", "allowed_ips.0", "192.168.0.12"),
resource.TestCheckResourceAttr("scalr_account_allowed_ips.test", "allowed_ips.1", "0.0.0.0/0"),
),
},
{
Config: testAccScalrAccountAllowedIps([]string{"0.0.0.0/0"}),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet("scalr_account_allowed_ips.test", "id"),
resource.TestCheckResourceAttr("scalr_account_allowed_ips.test", "allowed_ips.#", "1"),
resource.TestCheckResourceAttr("scalr_account_allowed_ips.test", "allowed_ips.0", "0.0.0.0/0"),
),
},
},
})
}

func TestAccScalrAccountAllowedIps_import(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccScalrAccountAllowedIps([]string{"192.168.0.12", "0.0.0.0/0"}),
},

{
ResourceName: "scalr_account_allowed_ips.test",
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccScalrAccountAllowedIps_empty(t *testing.T) {
rg, _ := regexp.Compile(`config is invalid: allowed_ips: attribute supports 1 item as a minimum, config has 0 declared`)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccScalrAccountAllowedIps([]string{}),
ExpectError: rg,
},
},
})
}

func TestAccScalrAccountAllowedIps_invalid_CIDR(t *testing.T) {
rg, _ := regexp.Compile(`value is not a valid IPv4 network`)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccScalrAccountAllowedIps([]string{"192.168.0.12/24"}),
ExpectError: rg,
},
},
})
}

func testAccScalrAccountAllowedIps(allowedIps []string) string {
ips, _ := json.Marshal(allowedIps)
return fmt.Sprintf(`
resource "scalr_account_allowed_ips" "test" {
account_id = "%s"
allowed_ips = %s
}`, defaultAccount, ips)
}

0 comments on commit 55a4237

Please sign in to comment.