Skip to content

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 41 commits into
base: master
Choose a base branch
from

Conversation

nkonjeti
Copy link

@nkonjeti nkonjeti commented Apr 16, 2025

Motivation:

We wanted a terraform provider to interact with our Custom Framework APIs. The terraform resources will remain as the source of truth so whenever a resource is updated/created/deleted this will be reflected in the database and UI.

Testing

I built the Terrraform provider locally.

Tested the following:

Creating
Screenshot 2025-04-25 at 4 36 07 PM

Updating
Screenshot 2025-04-25 at 4 37 17 PM

Deleting/Destroying
Screenshot 2025-04-25 at 4 38 52 PM

Changing order of rules or requirements
Screenshot 2025-04-27 at 4 07 16 PM
no changes in state so no action taken

Commands:

cd examples/resources/datadog_custom_framework 
terraform init
terraform plan -var="datadog_api_key=<>" -var="datadog_app_key=<>"
terraform apply

also added unit tests!

@nkonjeti nkonjeti requested review from a team as code owners April 16, 2025 22:27
@nkonjeti nkonjeti changed the title provider for custom frameworks Terraform Provider for Custom Frameworks Apr 24, 2025
@nkonjeti nkonjeti force-pushed the neha.konjeti/framework-provider branch from ccaf302 to f8a90fe Compare April 25, 2025 18:59
@nkonjeti nkonjeti marked this pull request as draft April 25, 2025 20:34

- `name` (String) The name of the requirement.

Optional:
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It auto generates as optional even though I have a validator in the schema requiring controls

@nkonjeti nkonjeti changed the title Terraform Provider for Custom Frameworks [K9VULN-4477]: Terraform Provider for Custom Frameworks Apr 30, 2025
@nkonjeti nkonjeti marked this pull request as ready for review May 1, 2025 21:14
@nkonjeti nkonjeti requested a review from a team as a code owner May 1, 2025 21:14
@nkonjeti nkonjeti changed the title [K9VULN-4477]: Terraform Provider for Custom Frameworks Terraform Provider for Custom Frameworks May 2, 2025
@nkonjeti nkonjeti force-pushed the neha.konjeti/framework-provider branch from ac03605 to e2e496e Compare May 14, 2025 17:25
@nkonjeti nkonjeti requested a review from vbarth2 May 14, 2025 17:37
@nkonjeti nkonjeti force-pushed the neha.konjeti/framework-provider branch from 7da9031 to e768276 Compare May 14, 2025 17:42
data, _, err := r.Api.GetCustomFramework(r.Auth, state.Handle.ValueString(), state.Version.ValueString())
// If the framework does not exist, remove it from terraform state
// This is to avoid the provider to return an error when the framework is deleted in the UI prior
if err != nil && err.Error() == "400 Bad Request" {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any other scenarios that throw a 400? Can we case on something else?

Copy link
Author

@nkonjeti nkonjeti May 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For GetCustomFramework the other ways to get a 400 is if the handle or version is empty. But the terraform validator already checks that - so the only way to get a 400 if the framework doesn't exist

The framework wouldn't be in the state if it failed validation during Create so in Read it would have already been validated.

func (r *complianceCustomFrameworkResource) ImportState(ctx context.Context, request resource.ImportStateRequest, response *resource.ImportStateResponse) {
// Split the ID into handle and version
// The last hyphen separates handle and version
lastHyphenIndex := strings.LastIndex(request.ID, "-")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we directly pass the handle and version instead of splitting the id

Copy link
Author

@nkonjeti nkonjeti May 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately the only way to do it using the id. I can see how this can be a problem so I removed the import functionality

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think instead of importing the resource - because we can't directly pass in the handle and version I will change the create function to update the framework if the handle and version exists.

for _, requirement := range req.ConfigValue.Elements() {
reqObj := requirement.(types.Object)
name := reqObj.Attributes()["name"].(types.String).ValueString()
log.Printf("Found requirement name in config: %s", name)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add [INFO] in front of the non-error logs

name = "requirement1"
controls {
name = "control1"
rules_id = ["aaa-000-ccc", "bbb-000-ddd"]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use real rule IDs here

@nkonjeti nkonjeti requested a review from vbarth2 May 16, 2025 17:50
response.State.RemoveResource(ctx)
return
}
if err != nil {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we switch the order of this and the if block on 182

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually I can't switch the ordering because if I check if err is not nil without checking the status code first it would return before checking if the status code is 400

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants