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

Merged
merged 54 commits into from
May 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
f677c3e
provider for custom frameworks
nkonjeti Apr 16, 2025
10203d3
passed set create test
nkonjeti Apr 24, 2025
1ff1b16
clean up code and add tests
nkonjeti Apr 24, 2025
caa0f70
add invalid create framework tests
nkonjeti Apr 24, 2025
9016c43
test files
nkonjeti Apr 24, 2025
b234607
add import state functionality
nkonjeti Apr 25, 2025
c86d37f
update mod file
nkonjeti Apr 25, 2025
db0ac70
clean up code
nkonjeti Apr 25, 2025
07fa958
update tests
nkonjeti Apr 25, 2025
bd39a39
test update is not triggered if order is changed
nkonjeti Apr 27, 2025
eef04ab
change retrieve custom framework to get custom framework
nkonjeti Apr 28, 2025
4a4b65e
update api spec in go mod
nkonjeti Apr 29, 2025
b8a3336
add docs for terraform provider
nkonjeti Apr 29, 2025
a9eca77
remove unstable endpoint
nkonjeti Apr 29, 2025
c9efcf8
add more tests
nkonjeti Apr 29, 2025
ad9ad89
add validators
nkonjeti May 1, 2025
efd64be
change tests to use same handle and version
nkonjeti May 1, 2025
7a3966a
add test for 409 conflict
nkonjeti May 1, 2025
5925d17
add a resource file
nkonjeti May 5, 2025
9c73be6
add example in doc and remove comments
nkonjeti May 6, 2025
5717ad5
fix required requirements and control block
nkonjeti May 7, 2025
9941c0d
changeexample to compliance custom framework
nkonjeti May 7, 2025
8a49998
fix docs
nkonjeti May 7, 2025
70c34e9
make icon url optional and remove description
nkonjeti May 14, 2025
5a26389
add comment to describe why requirements is a set
nkonjeti May 14, 2025
eec537f
remove description from resource example
nkonjeti May 14, 2025
6b9916c
remove comments and extra cassettes
nkonjeti May 14, 2025
246f5a4
fix description of icon url
nkonjeti May 14, 2025
c8c33c3
fix format
nkonjeti May 14, 2025
4edfbc2
delete framework in conflict test
nkonjeti May 15, 2025
41767fa
remove import resource and update when create conflicts
nkonjeti May 16, 2025
5376a3c
use real rule ids in the example resource
nkonjeti May 16, 2025
311ecab
remove logs
nkonjeti May 16, 2025
35462c1
test same state framework id
nkonjeti May 16, 2025
2314db8
add better comments for delete after delete case
nkonjeti May 16, 2025
99334a5
add cassetes for same config no update test
nkonjeti May 16, 2025
df44203
move around error handling
nkonjeti May 16, 2025
fcc02b3
Revert "move around error handling"
nkonjeti May 16, 2025
e0c42cd
remove err check
nkonjeti May 16, 2025
bb27a3b
add invalidcreate cassettes
nkonjeti May 16, 2025
ec79449
use real rule ids
nkonjeti May 18, 2025
b21e347
RecreateAfterAPIDelete cassettes
nkonjeti May 19, 2025
75cd14e
add immutable fields edge case
nkonjeti May 19, 2025
1549d3b
change requirements and controls to lists'
nkonjeti May 21, 2025
6443f00
fix modify plan
nkonjeti May 21, 2025
b36f334
fix the apply issue
nkonjeti May 21, 2025
827f4a9
remove modify plan because read API response order is changed
nkonjeti May 22, 2025
2ea549a
remove import file
nkonjeti May 22, 2025
b484257
check for rule ids length and update docs
nkonjeti May 22, 2025
014a599
remove same config no update test
nkonjeti May 22, 2025
7291b2d
use one validator and add test for duplicate handle
nkonjeti May 27, 2025
a432275
edit validator file name
nkonjeti May 27, 2025
7d583c8
update doc
nkonjeti May 27, 2025
e0c1c11
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 @@ -88,6 +88,7 @@ var Resources = []func() resource.Resource{
NewOnCallScheduleResource,
NewOnCallTeamRoutingRulesResource,
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