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

Add terraform resource and data source changes to support infrastructure application #4128

Merged
merged 30 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
02030c0
Add terraform resource and data source changes to support infrastruct…
SaiDadireddy Sep 26, 2024
6a2dc7f
Add connection rules access policy acc test
SaiDadireddy Sep 26, 2024
1e7e2c6
Add missing docs and update schema
SaiDadireddy Sep 26, 2024
ba923a1
nit: add new line
SaiDadireddy Sep 26, 2024
05eb203
add hostname list
SaiDadireddy Sep 26, 2024
75018be
fixup access app resource
SaiDadireddy Sep 26, 2024
7456e2b
update connection rules schema
SaiDadireddy Sep 26, 2024
82ab482
update connection rules helper functions
SaiDadireddy Sep 26, 2024
16f8431
Add test case for infrastructure application
SaiDadireddy Sep 26, 2024
49456d7
Parse data schema to struct
SaiDadireddy Sep 26, 2024
4226ac9
Fix update bug
SaiDadireddy Sep 26, 2024
0d64553
Add descriptions to schema
SaiDadireddy Sep 26, 2024
2427b78
Update tests
SaiDadireddy Sep 26, 2024
64e74e2
Add examples
SaiDadireddy Sep 26, 2024
5c2f980
nit
SaiDadireddy Sep 26, 2024
5038c40
remove dev override in go.mod
SaiDadireddy Sep 26, 2024
561038d
value -> values
SaiDadireddy Sep 26, 2024
24209c1
fix tests
SaiDadireddy Sep 26, 2024
1cba553
fix tests
SaiDadireddy Sep 26, 2024
8009d75
Update how lists are formed
SaiDadireddy Sep 27, 2024
e13fad4
add changelog
SaiDadireddy Sep 27, 2024
e24ca92
Rename 4127.txt to 4128.txt
jacobbednarz Sep 27, 2024
e8e2c18
handle 0 sized values for connection rules
jacobbednarz Sep 27, 2024
991890b
fix test assertion notation
jacobbednarz Sep 27, 2024
e301877
safeguard for empty values
jacobbednarz Sep 27, 2024
6238847
suppress the diff for `infrastructure` app types
jacobbednarz Sep 27, 2024
1ada843
Merge branch 'master' into sdadireddy/infra-app
jacobbednarz Sep 27, 2024
42321e0
rename structs
jacobbednarz Sep 27, 2024
a58a345
fix lint
jacobbednarz Sep 27, 2024
5bd631a
`make docs`
jacobbednarz Sep 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changelog/4128.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:enhancement
resource/cloudflare_access_application: added target contexts support for access application type infrastructure
```

```release-note:enhancement
resource/cloudflare_access_policy: added infrastructure connection rule support for access policy
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
data "cloudflare_infrastructure_access_targets" "example" {
account_id = "f037e56e89293a057740de681ac9abbe"
# Query parameters
hostname_contains = "example"
ipv4 = "210.26.29.230"
}

# output the list of targets the data source contains
output "targets" {
value = data.cloudflare_infrastructure_access_targets.example.targets
}
19 changes: 19 additions & 0 deletions examples/resources/cloudflare_access_application/resource.tf
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,22 @@ resource "cloudflare_access_application" "staging_app" {
max_age = 10
}
}

# Infrastructure application configuration
resource "cloudflare_zero_trust_access_application" "infra-app-example" {
account_id = "0da42c8d2132a9ddaf714f9e7c920711"
name = "infra-app"
type = "infrastructure"

target_criteria {
port = 22
protocol = "SSH"
target_attributes {
name = "hostname"
values = ["tfgo-tests-useast", "tfgo-tests-uswest"]
}
}

# specify existing access policies by id
policies = []
}
38 changes: 38 additions & 0 deletions examples/resources/cloudflare_access_policy/resource.tf
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,41 @@ resource "cloudflare_access_policy" "test_policy" {
ip = [var.office_ip]
}
}

# Access policy for an infrastructure application
resource "cloudflare_access_policy" "infra-app-example-allow" {
application_id = cloudflare_zero_trust_access_application.infra-app-example.id
account_id = "0da42c8d2132a9ddaf714f9e7c920711"
name = "infra-app-example-allow"
decision = "allow"
precedence = 1

include {
email = ["[email protected]"]
}

connection_rules {
ssh {
usernames = ["ec2-user"]
}
}
}

# Infrastructure application configuration for infra-app-example-allow
resource "cloudflare_zero_trust_access_application" "infra-app-example" {
account_id = "0da42c8d2132a9ddaf714f9e7c920711"
name = "infra-app"
type = "infrastructure"

target_criteria {
port = 22
protocol = "SSH"
target_attributes {
name = "hostname"
values = ["tfgo-tests-useast", "tfgo-tests-uswest"]
}
}

# specify existing access policies by id
policies = []
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
$ terraform import cloudflare_infrastructure_access_target.example <account_id>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
resource "cloudflare_infrastructure_access_target" "example" {
account_id = "f037e56e89293a057740de681ac9abbe"
hostname = "example-target"
ip = {
ipv4 = {
ip_addr = "210.26.29.230"
virtual_network_id = "238dccd1-149b-463d-8228-560ab83a54fd"
}
ipv6 = {
ip_addr = "24c0:64e8:f0b4:8dbf:7104:72b0:ef8f:f5e0"
virtual_network_id = "238dccd1-149b-463d-8228-560ab83a54fd"
}
}
}

resource "cloudflare_infrastructure_access_target" "ipv4_only_example" {
account_id = "f037e56e89293a057740de681ac9abbe"
hostname = "example-ipv4-only"
ip = {
ipv4 = {
ip_addr = "210.26.29.230"
virtual_network_id = "238dccd1-149b-463d-8228-560ab83a54fd"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,14 @@
newAccessApplication.SaasApplication = convertSaasSchemaToStruct(d)
}

if _, ok := d.GetOk("target_criteria"); ok {
target_contexts, err := convertTargetContextsToStruct(d)
if err != nil {
return diag.FromErr(err)
}
newAccessApplication.TargetContexts = target_contexts

Check failure on line 111 in internal/sdkv2provider/resource_cloudflare_access_application.go

View workflow job for this annotation

GitHub Actions / test

newAccessApplication.TargetContexts undefined (type cloudflare.CreateAccessApplicationParams has no field or method TargetContexts)

Check failure on line 111 in internal/sdkv2provider/resource_cloudflare_access_application.go

View workflow job for this annotation

GitHub Actions / golangci-lint

newAccessApplication.TargetContexts undefined (type cloudflare.CreateAccessApplicationParams has no field or method TargetContexts)

Check failure on line 111 in internal/sdkv2provider/resource_cloudflare_access_application.go

View workflow job for this annotation

GitHub Actions / tfproviderlint (ubuntu-latest)

newAccessApplication.TargetContexts undefined (type cloudflare.CreateAccessApplicationParams has no field or method TargetContexts)
}

if value, ok := d.GetOk("tags"); ok {
newAccessApplication.Tags = expandInterfaceToStringList(value.(*schema.Set).List())
}
Expand Down Expand Up @@ -244,6 +252,11 @@
return diag.FromErr(fmt.Errorf("error setting Access Application SaaS app configuration: %w", saasConfigErr))
}

targetContexts := convertTargetContextsToSchema(accessApplication.TargetContexts)

Check failure on line 255 in internal/sdkv2provider/resource_cloudflare_access_application.go

View workflow job for this annotation

GitHub Actions / test

accessApplication.TargetContexts undefined (type cloudflare.AccessApplication has no field or method TargetContexts)

Check failure on line 255 in internal/sdkv2provider/resource_cloudflare_access_application.go

View workflow job for this annotation

GitHub Actions / golangci-lint

accessApplication.TargetContexts undefined (type cloudflare.AccessApplication has no field or method TargetContexts)

Check failure on line 255 in internal/sdkv2provider/resource_cloudflare_access_application.go

View workflow job for this annotation

GitHub Actions / tfproviderlint (ubuntu-latest)

accessApplication.TargetContexts undefined (type cloudflare.AccessApplication has no field or method TargetContexts)
if targetContextsErr := d.Set("target_criteria", targetContexts); targetContextsErr != nil {
return diag.FromErr(fmt.Errorf("error setting Access Application Infrastructure app configuration: %w", targetContextsErr))
}

if _, ok := d.GetOk("self_hosted_domains"); ok {
d.Set("self_hosted_domains", accessApplication.SelfHostedDomains)
}
Expand Down Expand Up @@ -328,6 +341,14 @@
updatedAccessApplication.SaasApplication = saasConfig
}

if _, ok := d.GetOk("target_criteria"); ok {
target_contexts, err := convertTargetContextsToStruct(d)
if err != nil {
return diag.FromErr(err)
}
updatedAccessApplication.TargetContexts = target_contexts

Check failure on line 349 in internal/sdkv2provider/resource_cloudflare_access_application.go

View workflow job for this annotation

GitHub Actions / test

updatedAccessApplication.TargetContexts undefined (type cloudflare.UpdateAccessApplicationParams has no field or method TargetContexts)

Check failure on line 349 in internal/sdkv2provider/resource_cloudflare_access_application.go

View workflow job for this annotation

GitHub Actions / tfproviderlint (ubuntu-latest)

updatedAccessApplication.TargetContexts undefined (type cloudflare.UpdateAccessApplicationParams has no field or method TargetContexts)
}

if value, ok := d.GetOk("tags"); ok {
updatedAccessApplication.Tags = expandInterfaceToStringList(value.(*schema.Set).List())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,34 @@ func TestAccCloudflareAccessApplication_WithAppLauncherVisible(t *testing.T) {
})
}

func TestAccCloudflareAccessApplication_WithTargetContexts(t *testing.T) {
rnd := generateRandomResourceName()
name := fmt.Sprintf("cloudflare_zero_trust_access_application.%s", rnd)

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
testAccPreCheckAccount(t)
},
ProviderFactories: providerFactories,
CheckDestroy: testAccCheckCloudflareAccessApplicationDestroy,
Steps: []resource.TestStep{
{
Config: testAccCloudflareAccessApplicationWithTargetContexts(rnd, domain, cloudflare.AccountIdentifier(accountID)),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(name, consts.AccountIDSchemaKey, accountID),
resource.TestCheckResourceAttr(name, "name", rnd),
resource.TestCheckResourceAttr(name, "type", "infrastructure"),
resource.TestCheckResourceAttr(name, "target_criteria.0.port", "22"),
resource.TestCheckResourceAttr(name, "target_criteria.0.protocol", "SSH"),
resource.TestCheckResourceAttr(name, "target_criteria.0.target_attributes.0.name", "hostname"),
resource.TestCheckResourceAttr(name, "target_criteria.0.target_attributes.0.values.0", "tfgo-acc-test"),
),
},
},
})
}

func TestAccCloudflareAccessApplication_WithSelfHostedDomains(t *testing.T) {
rnd := generateRandomResourceName()
name := fmt.Sprintf("cloudflare_zero_trust_access_application.%s", rnd)
Expand Down Expand Up @@ -1396,6 +1424,24 @@ func testAccessApplicationWithAppLauncherCustomizationFields(rnd, accountID stri
`, rnd, accountID)
}

func testAccCloudflareAccessApplicationWithTargetContexts(rnd string, domain string, identifier *cloudflare.ResourceContainer) string {
return fmt.Sprintf(`
resource "cloudflare_zero_trust_access_application" "%[1]s" {
%[3]s_id = "%[4]s"
name = "%[1]s"
type = "infrastructure"
target_criteria {
port = 22
protocol = "SSH"
target_attributes {
name = "hostname"
values = ["tfgo-acc-test"]
}
}
}
`, rnd, domain, identifier.Type, identifier.Identifier)
}

func testAccCloudflareAccessApplicationWithSelfHostedDomains(rnd string, domain string, identifier *cloudflare.ResourceContainer) string {
return fmt.Sprintf(`
resource "cloudflare_zero_trust_access_application" "%[1]s" {
Expand Down
62 changes: 62 additions & 0 deletions internal/sdkv2provider/resource_cloudflare_access_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,46 @@
return data
}

func schemaAccessPolicyConnectionRulesToAPI(connectionRules map[string]interface{}) (*cloudflare.InfraConnectionRules, error) {

Check failure on line 67 in internal/sdkv2provider/resource_cloudflare_access_policy.go

View workflow job for this annotation

GitHub Actions / test

undefined: cloudflare.InfraConnectionRules

Check failure on line 67 in internal/sdkv2provider/resource_cloudflare_access_policy.go

View workflow job for this annotation

GitHub Actions / golangci-lint

undefined: cloudflare.InfraConnectionRules

Check failure on line 67 in internal/sdkv2provider/resource_cloudflare_access_policy.go

View workflow job for this annotation

GitHub Actions / tfproviderlint (ubuntu-latest)

undefined: cloudflare.InfraConnectionRules
usernames := []string{}
if sshVal, ok := connectionRules["ssh"].([]interface{}); ok && len(sshVal) > 0 {
if sshMap, ok := sshVal[0].(map[string]interface{}); ok {
str_return := []string{}
if usernamesMap, ok := sshMap["usernames"].([]interface{}); ok {
for _, username := range usernamesMap {
str_return = append(str_return, username.(string))
}
}
usernames = str_return
}
}

return &cloudflare.InfraConnectionRules{

Check failure on line 81 in internal/sdkv2provider/resource_cloudflare_access_policy.go

View workflow job for this annotation

GitHub Actions / test

undefined: cloudflare.InfraConnectionRules

Check failure on line 81 in internal/sdkv2provider/resource_cloudflare_access_policy.go

View workflow job for this annotation

GitHub Actions / golangci-lint

undefined: cloudflare.InfraConnectionRules

Check failure on line 81 in internal/sdkv2provider/resource_cloudflare_access_policy.go

View workflow job for this annotation

GitHub Actions / tfproviderlint (ubuntu-latest)

undefined: cloudflare.InfraConnectionRules
SSH: &cloudflare.InfraConnectionRulesSSH{

Check failure on line 82 in internal/sdkv2provider/resource_cloudflare_access_policy.go

View workflow job for this annotation

GitHub Actions / test

undefined: cloudflare.InfraConnectionRulesSSH

Check failure on line 82 in internal/sdkv2provider/resource_cloudflare_access_policy.go

View workflow job for this annotation

GitHub Actions / golangci-lint

undefined: cloudflare.InfraConnectionRulesSSH

Check failure on line 82 in internal/sdkv2provider/resource_cloudflare_access_policy.go

View workflow job for this annotation

GitHub Actions / tfproviderlint (ubuntu-latest)

undefined: cloudflare.InfraConnectionRulesSSH
Usernames: usernames,
},
}, nil
}

func apiAccessPolicyConnectionRulesToSchema(connectionRules *cloudflare.InfraConnectionRules) []interface{} {

Check failure on line 88 in internal/sdkv2provider/resource_cloudflare_access_policy.go

View workflow job for this annotation

GitHub Actions / test

undefined: cloudflare.InfraConnectionRules

Check failure on line 88 in internal/sdkv2provider/resource_cloudflare_access_policy.go

View workflow job for this annotation

GitHub Actions / golangci-lint

undefined: cloudflare.InfraConnectionRules

Check failure on line 88 in internal/sdkv2provider/resource_cloudflare_access_policy.go

View workflow job for this annotation

GitHub Actions / tfproviderlint (ubuntu-latest)

undefined: cloudflare.InfraConnectionRules
if connectionRules == nil {
return []interface{}{}
}

var connectionRulesSchema []interface{}
var usernameList []map[string]interface{}

usernameMap := map[string]interface{}{
"usernames": connectionRules.SSH.Usernames,
}
usernameList = append(usernameList, usernameMap)
connectionRulesSchema = append(connectionRulesSchema, map[string]interface{}{
"ssh": usernameList,
})

return connectionRulesSchema
}

func schemaAccessPolicyApprovalGroupToAPI(data map[string]interface{}) cloudflare.AccessApprovalGroup {
var approvalGroup cloudflare.AccessApprovalGroup

Expand All @@ -87,6 +127,10 @@
d.Set("name", accessPolicy.Name)
d.Set("decision", accessPolicy.Decision)

if err := d.Set("connection_rules", apiAccessPolicyConnectionRulesToSchema(accessPolicy.InfraConnectionRules)); err != nil {

Check failure on line 130 in internal/sdkv2provider/resource_cloudflare_access_policy.go

View workflow job for this annotation

GitHub Actions / test

accessPolicy.InfraConnectionRules undefined (type cloudflare.AccessPolicy has no field or method InfraConnectionRules)

Check failure on line 130 in internal/sdkv2provider/resource_cloudflare_access_policy.go

View workflow job for this annotation

GitHub Actions / golangci-lint

accessPolicy.InfraConnectionRules undefined (type cloudflare.AccessPolicy has no field or method InfraConnectionRules)

Check failure on line 130 in internal/sdkv2provider/resource_cloudflare_access_policy.go

View workflow job for this annotation

GitHub Actions / tfproviderlint (ubuntu-latest)

accessPolicy.InfraConnectionRules undefined (type cloudflare.AccessPolicy has no field or method InfraConnectionRules)
return diag.FromErr(fmt.Errorf("failed to set connection_rules attribute: %w", err))
}

if err := d.Set("require", TransformAccessGroupForSchema(ctx, accessPolicy.Require)); err != nil {
return diag.FromErr(fmt.Errorf("failed to set require attribute: %w", err))
}
Expand Down Expand Up @@ -166,6 +210,15 @@
return diag.FromErr(fmt.Errorf("application_id is required for non-account level Access Policies"))
}

connectionRulesSchema, ok := d.Get("connection_rules").([]interface{})
if ok {
jacobbednarz marked this conversation as resolved.
Show resolved Hide resolved
connectionRules, err := schemaAccessPolicyConnectionRulesToAPI(connectionRulesSchema[0].(map[string]interface{}))
if err != nil {
return diag.FromErr(err)
}
newAccessPolicy.InfraConnectionRules = connectionRules
}

exclude := d.Get("exclude").([]interface{})
for _, value := range exclude {
if value != nil {
Expand Down Expand Up @@ -230,6 +283,15 @@
SessionDuration: cloudflare.StringPtr(d.Get("session_duration").(string)),
}

connectionRulesSchema, ok := d.Get("connection_rules").([]interface{})
if ok {
connectionRules, err := schemaAccessPolicyConnectionRulesToAPI(connectionRulesSchema[0].(map[string]interface{}))
if err != nil {
return diag.FromErr(err)
}
updateReq.InfraConnectionRules = connectionRules
}

exclude := d.Get("exclude").([]interface{})
for _, value := range exclude {
if value != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -972,6 +972,68 @@ func testAccessPolicyExternalEvalautionConfig(resourceID, zone, accountID string
`, resourceID, zone, accountID)
}

func TestAccCloudflareAccessPolicy_ConnectionRules(t *testing.T) {
rnd := generateRandomResourceName()
name := "cloudflare_access_policy." + rnd
zone := os.Getenv("CLOUDFLARE_DOMAIN")
accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID")

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
testAccPreCheckAccount(t)
},
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: testAccessPolicyConnectionRulesConfig(rnd, zone, accountID),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(name, "name", rnd),
resource.TestCheckResourceAttr(name, consts.AccountIDSchemaKey, accountID),
resource.TestCheckResourceAttr(name, "connection_rules.ssh.usernames.0", "tfgo-acc-test"),
resource.TestCheckResourceAttr(name, "include.0.email.0", "[email protected]"),
),
},
},
})
}

func testAccessPolicyConnectionRulesConfig(resourceID, zone, accountID string) string {
return fmt.Sprintf(`
resource "cloudflare_zero_trust_access_application" "%[1]s" {
name = "%[1]s"
type = "infrastructure"
account_id = "%[3]s"
domain = "%[1]s.%[2]s"
target_criteria {
port = 22
protocol = "SSH"
target_attributes {
name = "hostname"
values = ["tfgo-acc-test"]
}
}
}

resource "cloudflare_access_policy" "%[1]s" {
application_id = cloudflare_zero_trust_access_application.%[1]s.id
name = "%[1]s"
account_id = "%[3]s"
decision = "allow"
precedence = "1"
connection_rules {
ssh {
usernames = ["tfgo-acc-test"]
}
}
include {
email = ["[email protected]"]
}
}

`, resourceID, zone, accountID)
}

func TestAccCloudflareAccessPolicy_IsolationRequired(t *testing.T) {
rnd := generateRandomResourceName()
name := "cloudflare_access_policy." + rnd
Expand Down
Loading
Loading