-
Notifications
You must be signed in to change notification settings - Fork 636
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WDAPI-645 add a device posture integration
- Loading branch information
andrew
committed
Jan 3, 2022
1 parent
4676e55
commit 884de6e
Showing
7 changed files
with
426 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
185 changes: 185 additions & 0 deletions
185
cloudflare/resource_cloudflare_device_posture_integration.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
package cloudflare | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log" | ||
"strings" | ||
|
||
"github.com/cloudflare/cloudflare-go" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
) | ||
|
||
const ws1 = "workspace_one" | ||
|
||
func resourceCloudflareDevicePostureIntegration() *schema.Resource { | ||
return &schema.Resource{ | ||
Schema: resourceCloudflareDevicePostureIntegrationSchema(), | ||
Create: resourceCloudflareDevicePostureIntegrationCreate, | ||
Read: resourceCloudflareDevicePostureIntegrationRead, | ||
Update: resourceCloudflareDevicePostureIntegrationUpdate, | ||
Delete: resourceCloudflareDevicePostureIntegrationDelete, | ||
Importer: &schema.ResourceImporter{ | ||
State: resourceCloudflareDevicePostureIntegrationImport, | ||
}, | ||
} | ||
} | ||
|
||
func resourceCloudflareDevicePostureIntegrationCreate(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*cloudflare.API) | ||
accountID := d.Get("account_id").(string) | ||
|
||
newDevicePostureIntegration := cloudflare.DevicePostureIntegration{ | ||
Name: d.Get("name").(string), | ||
Type: d.Get("type").(string), | ||
Interval: d.Get("interval").(string), | ||
} | ||
|
||
err := setDevicePostureIntegrationConfig(&newDevicePostureIntegration, d) | ||
if err != nil { | ||
return fmt.Errorf("error creating Device Posture integration with provided config: %s", err) | ||
} | ||
fmt.Printf("[DEBUG] Creating Cloudflare Device Posture Integration from struct: %+v\n", newDevicePostureIntegration) | ||
|
||
// The API does not return the client_secret so it must be stored in the state func on resource create. | ||
savedSecret := newDevicePostureIntegration.Config.ClientSecret | ||
|
||
newDevicePostureIntegration, err = client.CreateDevicePostureIntegration(context.Background(), accountID, newDevicePostureIntegration, false) | ||
if err != nil { | ||
return fmt.Errorf("error creating Device Posture Rule for account %q: %s %+v", accountID, err, newDevicePostureIntegration) | ||
} | ||
|
||
d.SetId(newDevicePostureIntegration.IntegrationID) | ||
|
||
return devicePostureIntegrationReadHelper(d, meta, savedSecret) | ||
} | ||
|
||
func resourceCloudflareDevicePostureIntegrationRead(d *schema.ResourceData, meta interface{}) error { | ||
// Client secret is always read from the local state. | ||
secret, _ := d.Get("config.0.client_secret").(string) | ||
return devicePostureIntegrationReadHelper(d, meta, secret) | ||
} | ||
|
||
func devicePostureIntegrationReadHelper(d *schema.ResourceData, meta interface{}, secret string) error { | ||
client := meta.(*cloudflare.API) | ||
accountID := d.Get("account_id").(string) | ||
|
||
devicePostureIntegration, err := client.DevicePostureIntegration(context.Background(), accountID, d.Id()) | ||
if err != nil { | ||
if strings.Contains(err.Error(), "HTTP status 404") { | ||
log.Printf("[INFO] Device posture integration %s no longer exists", d.Id()) | ||
d.SetId("") | ||
return nil | ||
} | ||
return fmt.Errorf("error finding device posture integration %q: %s", d.Id(), err) | ||
} | ||
|
||
devicePostureIntegration.Config.ClientSecret = secret | ||
d.Set("name", devicePostureIntegration.Name) | ||
d.Set("type", devicePostureIntegration.Type) | ||
d.Set("interval", devicePostureIntegration.Interval) | ||
d.Set("config", convertIntegrationConfigToSchema(devicePostureIntegration.Config)) | ||
|
||
return nil | ||
} | ||
|
||
func resourceCloudflareDevicePostureIntegrationUpdate(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*cloudflare.API) | ||
accountID := d.Get("account_id").(string) | ||
|
||
updatedDevicePostureIntegration := cloudflare.DevicePostureIntegration{ | ||
IntegrationID: d.Id(), | ||
Name: d.Get("name").(string), | ||
Type: d.Get("type").(string), | ||
Interval: d.Get("interval").(string), | ||
} | ||
|
||
err := setDevicePostureIntegrationConfig(&updatedDevicePostureIntegration, d) | ||
if err != nil { | ||
return fmt.Errorf("error creating Device Posture Rule with provided match input: %s", err) | ||
} | ||
|
||
log.Printf("[DEBUG] Updating Cloudflare device posture integration from struct: %+v", updatedDevicePostureIntegration) | ||
|
||
devicePostureIntegration, err := client.UpdateDevicePostureIntegration(context.Background(), accountID, updatedDevicePostureIntegration, false) | ||
if err != nil { | ||
return fmt.Errorf("error updating device posture integration for account %q: %s", accountID, err) | ||
} | ||
|
||
if devicePostureIntegration.IntegrationID == "" { | ||
return fmt.Errorf("failed to find device posture integration_id in update response; resource was empty") | ||
} | ||
|
||
return resourceCloudflareDevicePostureIntegrationRead(d, meta) | ||
} | ||
|
||
func resourceCloudflareDevicePostureIntegrationDelete(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*cloudflare.API) | ||
appID := d.Id() | ||
accountID := d.Get("account_id").(string) | ||
|
||
log.Printf("[DEBUG] Deleting Cloudflare device posture integration using ID: %s", appID) | ||
|
||
err := client.DeleteDevicePostureIntegration(context.Background(), accountID, appID) | ||
if err != nil { | ||
return fmt.Errorf("error deleting Device Posture Rule for account %q: %s", accountID, err) | ||
} | ||
|
||
resourceCloudflareDevicePostureIntegrationRead(d, meta) | ||
|
||
return nil | ||
} | ||
|
||
func resourceCloudflareDevicePostureIntegrationImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { | ||
attributes := strings.SplitN(d.Id(), "/", 2) | ||
|
||
if len(attributes) != 2 { | ||
return nil, fmt.Errorf("invalid id (\"%s\") specified, should be in format \"accountID/devicePostureIntegrationID\"", d.Id()) | ||
} | ||
|
||
accountID, devicePostureIntegrationID := attributes[0], attributes[1] | ||
|
||
log.Printf("[DEBUG] Importing Cloudflare device posture integration: id %s for account %s", devicePostureIntegrationID, accountID) | ||
|
||
d.Set("account_id", accountID) | ||
d.SetId(devicePostureIntegrationID) | ||
|
||
resourceCloudflareDevicePostureIntegrationRead(d, meta) | ||
|
||
return []*schema.ResourceData{d}, nil | ||
} | ||
|
||
func setDevicePostureIntegrationConfig(integration *cloudflare.DevicePostureIntegration, d *schema.ResourceData) error { | ||
if _, ok := d.GetOk("config"); ok { | ||
config := cloudflare.DevicePostureIntegrationConfig{} | ||
switch integration.Type { | ||
case ws1: | ||
if config.ClientID, ok = d.Get("config.0.client_id").(string); !ok { | ||
return fmt.Errorf("client_id is a string") | ||
} | ||
if config.ClientSecret, ok = d.Get("config.0.client_secret").(string); !ok { | ||
return fmt.Errorf("client_secret is a string") | ||
} | ||
if config.AuthUrl, ok = d.Get("config.0.auth_url").(string); !ok { | ||
return fmt.Errorf("auth_url is a string") | ||
} | ||
if config.ApiUrl, ok = d.Get("config.0.api_url").(string); !ok { | ||
return fmt.Errorf("api_url is a string") | ||
} | ||
integration.Config = config | ||
default: | ||
return fmt.Errorf("unsupported integration type:%s", integration.Type) | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func convertIntegrationConfigToSchema(input cloudflare.DevicePostureIntegrationConfig) []interface{} { | ||
m := map[string]interface{}{ | ||
"client_id": input.ClientID, | ||
"client_secret": input.ClientSecret, | ||
"auth_url": input.AuthUrl, | ||
"api_url": input.ApiUrl, | ||
} | ||
return []interface{}{m} | ||
} |
83 changes: 83 additions & 0 deletions
83
cloudflare/resource_cloudflare_device_posture_integration_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package cloudflare | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"os" | ||
"testing" | ||
|
||
"github.com/cloudflare/cloudflare-go" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform" | ||
) | ||
|
||
func TestAccCloudflareDevicePostureIntegrationCreate(t *testing.T) { | ||
// Temporarily unset CLOUDFLARE_API_TOKEN if it is set as the Access | ||
// service does not yet support the API tokens and it results in | ||
// misleading state error messages. | ||
if os.Getenv("CLOUDFLARE_API_TOKEN") != "" { | ||
defer func(apiToken string) { | ||
os.Setenv("CLOUDFLARE_API_TOKEN", apiToken) | ||
}(os.Getenv("CLOUDFLARE_API_TOKEN")) | ||
os.Setenv("CLOUDFLARE_API_TOKEN", "") | ||
} | ||
|
||
rnd := generateRandomResourceName() | ||
name := fmt.Sprintf("cloudflare_device_posture_integration.%s", rnd) | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { | ||
testAccessAccPreCheck(t) | ||
}, | ||
Providers: testAccProviders, | ||
CheckDestroy: testAccCheckCloudflareDevicePostureIntegrationDestroy, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccCloudflareDevicePostureIntegration(rnd, accountID), | ||
Check: resource.ComposeTestCheckFunc( | ||
resource.TestCheckResourceAttr(name, "account_id", accountID), | ||
resource.TestCheckResourceAttr(name, "name", rnd), | ||
resource.TestCheckResourceAttr(name, "type", "workspace_one"), | ||
resource.TestCheckResourceAttr(name, "interval", "24h"), | ||
resource.TestCheckResourceAttr(name, "config.0.auth_url", "https://test.uemauth.vmwservices.com/connect/token"), | ||
resource.TestCheckResourceAttr(name, "config.0.api_url", "https://example.com/api-url"), | ||
resource.TestCheckResourceAttr(name, "config.0.client_id", "client-id"), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccCloudflareDevicePostureIntegration(rnd, accountID string) string { | ||
return fmt.Sprintf(` | ||
resource "cloudflare_device_posture_integration" "%[1]s" { | ||
account_id = "%[2]s" | ||
name = "%[1]s" | ||
type = "workspace_one" | ||
interval = "24h" | ||
config { | ||
api_url = "https://example.com/api-url" | ||
auth_url = "https://test.uemauth.vmwservices.com/connect/token" | ||
client_id = "client-id" | ||
client_secret = "client-secret" | ||
} | ||
} | ||
`, rnd, accountID) | ||
} | ||
|
||
func testAccCheckCloudflareDevicePostureIntegrationDestroy(s *terraform.State) error { | ||
client := testAccProvider.Meta().(*cloudflare.API) | ||
|
||
for _, rs := range s.RootModule().Resources { | ||
if rs.Type != "cloudflare_device_posture_integration" { | ||
continue | ||
} | ||
|
||
_, err := client.DevicePostureIntegration(context.Background(), rs.Primary.Attributes["account_id"], rs.Primary.ID) | ||
if err == nil { | ||
return fmt.Errorf("Device Posture Integration still exists") | ||
} | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
63 changes: 63 additions & 0 deletions
63
cloudflare/schema_cloudflare_device_posture_integration.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package cloudflare | ||
|
||
import ( | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" | ||
) | ||
|
||
func resourceCloudflareDevicePostureIntegrationSchema() map[string]*schema.Schema { | ||
return map[string]*schema.Schema{ | ||
"account_id": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
"id": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
"name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
"type": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ValidateFunc: validation.StringInSlice([]string{ws1}, false), | ||
}, | ||
"identifier": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
}, | ||
"interval": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
}, | ||
"config": { | ||
Type: schema.TypeList, | ||
Optional: true, | ||
Elem: &schema.Resource{ | ||
Schema: map[string]*schema.Schema{ | ||
"auth_url": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ValidateFunc: validation.IsURLWithHTTPS, | ||
}, | ||
"api_url": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ValidateFunc: validation.IsURLWithHTTPS, | ||
}, | ||
"client_id": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
}, | ||
"client_secret": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Sensitive: true, | ||
}, | ||
}, | ||
}, | ||
}, | ||
} | ||
} |
Oops, something went wrong.