Skip to content

[datadog_compliance_custom_framework] Terraform Provider for Custom Frameworks #2975

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

Open
wants to merge 55 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
a444409
provider for custom frameworks
nkonjeti Apr 16, 2025
6f38f6a
passed set create test
nkonjeti Apr 24, 2025
2f56157
clean up code and add tests
nkonjeti Apr 24, 2025
6e8e30a
add invalid create framework tests
nkonjeti Apr 24, 2025
ebc825b
test files
nkonjeti Apr 24, 2025
0504d3a
add import state functionality
nkonjeti Apr 25, 2025
588e70f
update mod file
nkonjeti Apr 25, 2025
b4b31d4
clean up code
nkonjeti Apr 25, 2025
a93ec43
update tests
nkonjeti Apr 25, 2025
ed9aace
test update is not triggered if order is changed
nkonjeti Apr 27, 2025
f6a5907
change retrieve custom framework to get custom framework
nkonjeti Apr 28, 2025
9273dd7
update api spec in go mod
nkonjeti Apr 29, 2025
6da58e3
add docs for terraform provider
nkonjeti Apr 29, 2025
50e5b15
remove unstable endpoint
nkonjeti Apr 29, 2025
1ddc9b9
add more tests
nkonjeti Apr 29, 2025
f94c1ab
add validators
nkonjeti May 1, 2025
c042cf7
change tests to use same handle and version
nkonjeti May 1, 2025
5cbef69
add test for 409 conflict
nkonjeti May 1, 2025
fb2e9e4
add a resource file
nkonjeti May 5, 2025
7cf94a8
add example in doc and remove comments
nkonjeti May 6, 2025
1ace048
fix required requirements and control block
nkonjeti May 7, 2025
ebc74dc
changeexample to compliance custom framework
nkonjeti May 7, 2025
2c25611
fix docs
nkonjeti May 7, 2025
7df1175
update go mod
nkonjeti May 14, 2025
539285b
make icon url optional and remove description
nkonjeti May 14, 2025
92734e1
add comment to describe why requirements is a set
nkonjeti May 14, 2025
bd5a81c
remove description from resource example
nkonjeti May 14, 2025
c5c0c23
remove comments and extra cassettes
nkonjeti May 14, 2025
e768276
fix description of icon url
nkonjeti May 14, 2025
5081952
fix format
nkonjeti May 14, 2025
5042d13
delete framework in conflict test
nkonjeti May 15, 2025
6d56771
remove import resource and update when create conflicts
nkonjeti May 16, 2025
b420695
use real rule ids in the example resource
nkonjeti May 16, 2025
b744bca
remove logs
nkonjeti May 16, 2025
75d3526
test same state framework id
nkonjeti May 16, 2025
0e02eb5
add better comments for delete after delete case
nkonjeti May 16, 2025
1498f72
add cassetes for same config no update test
nkonjeti May 16, 2025
367c92c
move around error handling
nkonjeti May 16, 2025
830d9a4
Revert "move around error handling"
nkonjeti May 16, 2025
1111d5d
remove err check
nkonjeti May 16, 2025
ab1976b
add invalidcreate cassettes
nkonjeti May 16, 2025
1684e7e
use real rule ids
nkonjeti May 18, 2025
7f5d4b1
RecreateAfterAPIDelete cassettes
nkonjeti May 19, 2025
74992a6
add immutable fields edge case
nkonjeti May 19, 2025
7e74564
change requirements and controls to lists'
nkonjeti May 21, 2025
c1ec028
fix modify plan
nkonjeti May 21, 2025
f3ae423
fix the apply issue
nkonjeti May 21, 2025
3561635
remove modify plan because read API response order is changed
nkonjeti May 22, 2025
25f4df7
remove import file
nkonjeti May 22, 2025
424a1a8
check for rule ids length and update docs
nkonjeti May 22, 2025
c1e72f2
remove same config no update test
nkonjeti May 22, 2025
a7e30a2
use one validator and add test for duplicate handle
nkonjeti May 27, 2025
0c82f2b
edit validator file name
nkonjeti May 27, 2025
f4a8ebd
update doc
nkonjeti May 27, 2025
d7a9e26
updateifframework exists casettes
nkonjeti May 27, 2025
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
1 change: 1 addition & 0 deletions datadog/fwprovider/framework_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ var Resources = []func() resource.Resource{
NewAppBuilderAppResource,
NewObservabilitPipelineResource,
NewSecurityMonitoringRuleJSONResource,
NewComplianceCustomFrameworkResource,
}

var Datasources = []func() datasource.DataSource{
Expand Down
390 changes: 390 additions & 0 deletions datadog/fwprovider/resource_datadog_compliance_custom_framework.go

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package validators

import (
"context"
"fmt"

"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
)

type duplicateRequirementControlValidator struct{}

func (v duplicateRequirementControlValidator) Description(context.Context) string {
return "checks for duplicate requirement and control names"
}

func (v duplicateRequirementControlValidator) MarkdownDescription(ctx context.Context) string {
return v.Description(ctx)
}

func (v duplicateRequirementControlValidator) ValidateList(ctx context.Context, req validator.ListRequest, resp *validator.ListResponse) {
if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() {
return
}

seen := make(map[string]bool)
for _, requirement := range req.ConfigValue.Elements() {
reqObj := requirement.(types.Object)
name := reqObj.Attributes()["name"].(types.String).ValueString()
if seen[name] {
resp.Diagnostics.AddError(
"Each Requirement must have a unique name",
fmt.Sprintf("Requirement name '%s' is used more than once.", name),
)
return
}
seen[name] = true
controls := reqObj.Attributes()["controls"].(types.List)
controlNames := make(map[string]bool)
for _, control := range controls.Elements() {
ctrlObj := control.(types.Object)
ctrlName := ctrlObj.Attributes()["name"].(types.String).ValueString()
if controlNames[ctrlName] {
resp.Diagnostics.AddError(
"Each Control must have a unique name under the same requirement",
fmt.Sprintf("Control name '%s' is used more than once under requirement '%s'", ctrlName, name),
)
return
}
controlNames[ctrlName] = true
}
}
}

func DuplicateRequirementControlValidator() validator.List {
return &duplicateRequirementControlValidator{}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2025-05-22T10:29:37.579487-05:00
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
---
version: 2
interactions:
- id: 0
request:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
content_length: 385
transfer_encoding: []
trailer: {}
host: api.datadoghq.com
remote_addr: ""
request_uri: ""
body: |
{"data":{"attributes":{"handle":"terraform-handle","icon_url":"test url","name":"new-name","requirements":[{"controls":[{"name":"security-control","rules_id":["def-000-be9","def-000-cea"]}],"name":"security-requirement"},{"controls":[{"name":"compliance-control","rules_id":["def-000-be9","def-000-cea"]}],"name":"compliance-requirement"}],"version":"1.0"},"type":"custom_framework"}}
form: {}
headers:
Accept:
- application/json
Content-Type:
- application/json
url: https://api.datadoghq.com/api/v2/cloud_security_management/custom_frameworks
method: POST
response:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
transfer_encoding: []
trailer: {}
content_length: 123
uncompressed: false
body: '{"data":{"id":"terraform-handle-1.0","type":"custom_framework","attributes":{"handle":"terraform-handle","version":"1.0"}}}'
headers:
Content-Type:
- application/vnd.api+json
status: 200 OK
code: 200
duration: 1.052087125s
- id: 1
request:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
content_length: 0
transfer_encoding: []
trailer: {}
host: api.datadoghq.com
remote_addr: ""
request_uri: ""
body: ""
form: {}
headers:
Accept:
- application/json
url: https://api.datadoghq.com/api/v2/cloud_security_management/custom_frameworks/terraform-handle/1.0
method: GET
response:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
transfer_encoding: []
trailer: {}
content_length: 412
uncompressed: false
body: '{"data":{"id":"terraform-handle-1.0","type":"custom_framework","attributes":{"handle":"terraform-handle","icon_url":"test url","name":"new-name","requirements":[{"name":"compliance-requirement","controls":[{"name":"compliance-control","rules_id":["def-000-be9","def-000-cea"]}]},{"name":"security-requirement","controls":[{"name":"security-control","rules_id":["def-000-be9","def-000-cea"]}]}],"version":"1.0"}}}'
headers:
Content-Type:
- application/vnd.api+json
status: 200 OK
code: 200
duration: 911.326958ms
- id: 2
request:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
content_length: 0
transfer_encoding: []
trailer: {}
host: api.datadoghq.com
remote_addr: ""
request_uri: ""
body: ""
form: {}
headers:
Accept:
- application/json
url: https://api.datadoghq.com/api/v2/cloud_security_management/custom_frameworks/terraform-handle/1.0
method: GET
response:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
transfer_encoding: []
trailer: {}
content_length: 412
uncompressed: false
body: '{"data":{"id":"terraform-handle-1.0","type":"custom_framework","attributes":{"handle":"terraform-handle","icon_url":"test url","name":"new-name","requirements":[{"name":"compliance-requirement","controls":[{"name":"compliance-control","rules_id":["def-000-be9","def-000-cea"]}]},{"name":"security-requirement","controls":[{"name":"security-control","rules_id":["def-000-be9","def-000-cea"]}]}],"version":"1.0"}}}'
headers:
Content-Type:
- application/vnd.api+json
status: 200 OK
code: 200
duration: 250.824375ms
- id: 3
request:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
content_length: 483
transfer_encoding: []
trailer: {}
host: api.datadoghq.com
remote_addr: ""
request_uri: ""
body: |
{"data":{"attributes":{"handle":"terraform-handle","icon_url":"test url","name":"new-name","requirements":[{"controls":[{"name":"security-control","rules_id":["def-000-cea"]}],"name":"security-requirement"},{"controls":[{"name":"control","rules_id":["def-000-be9"]}],"name":"requirement"},{"controls":[{"name":"control-2","rules_id":["def-000-be9"]},{"name":"control-3","rules_id":["def-000-be9","def-000-cea"]}],"name":"requirement-2"}],"version":"1.0"},"type":"custom_framework"}}
form: {}
headers:
Accept:
- application/json
Content-Type:
- application/json
url: https://api.datadoghq.com/api/v2/cloud_security_management/custom_frameworks/terraform-handle/1.0
method: PUT
response:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
transfer_encoding: []
trailer: {}
content_length: 123
uncompressed: false
body: '{"data":{"id":"terraform-handle-1.0","type":"custom_framework","attributes":{"handle":"terraform-handle","version":"1.0"}}}'
headers:
Content-Type:
- application/vnd.api+json
status: 200 OK
code: 200
duration: 874.516417ms
- id: 4
request:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
content_length: 0
transfer_encoding: []
trailer: {}
host: api.datadoghq.com
remote_addr: ""
request_uri: ""
body: ""
form: {}
headers:
Accept:
- application/json
url: https://api.datadoghq.com/api/v2/cloud_security_management/custom_frameworks/terraform-handle/1.0
method: GET
response:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
transfer_encoding: []
trailer: {}
content_length: 510
uncompressed: false
body: '{"data":{"id":"terraform-handle-1.0","type":"custom_framework","attributes":{"handle":"terraform-handle","icon_url":"test url","name":"new-name","requirements":[{"name":"requirement","controls":[{"name":"control","rules_id":["def-000-be9"]}]},{"name":"requirement-2","controls":[{"name":"control-3","rules_id":["def-000-be9","def-000-cea"]},{"name":"control-2","rules_id":["def-000-be9"]}]},{"name":"security-requirement","controls":[{"name":"security-control","rules_id":["def-000-cea"]}]}],"version":"1.0"}}}'
headers:
Content-Type:
- application/vnd.api+json
status: 200 OK
code: 200
duration: 609.843333ms
- id: 5
request:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
content_length: 0
transfer_encoding: []
trailer: {}
host: api.datadoghq.com
remote_addr: ""
request_uri: ""
body: ""
form: {}
headers:
Accept:
- application/json
url: https://api.datadoghq.com/api/v2/cloud_security_management/custom_frameworks/terraform-handle/1.0
method: DELETE
response:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
transfer_encoding: []
trailer: {}
content_length: 180
uncompressed: false
body: '{"data":{"id":"terraform-handle-1.0","type":"custom_framework","attributes":{"description":"","handle":"terraform-handle","icon_url":"test url","name":"new-name","version":"1.0"}}}'
headers:
Content-Type:
- application/vnd.api+json
status: 200 OK
code: 200
duration: 492.356333ms
- id: 6
request:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
content_length: 0
transfer_encoding: []
trailer: {}
host: api.datadoghq.com
remote_addr: ""
request_uri: ""
body: ""
form: {}
headers:
Accept:
- application/json
url: https://api.datadoghq.com/api/v2/cloud_security_management/custom_frameworks/terraform-handle/1.0
method: GET
response:
proto: HTTP/1.1
proto_major: 1
proto_minor: 1
transfer_encoding: []
trailer: {}
content_length: 51
uncompressed: false
body: '{"errors":[{"status":"400","title":"Bad Request"}]}'
headers:
Content-Type:
- application/vnd.api+json
status: 400 Bad Request
code: 400
duration: 81.528292ms
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2025-05-22T10:28:47.103907-05:00
Loading
Loading