diff --git a/.changelog/1092.txt b/.changelog/1092.txt new file mode 100644 index 000000000..c6571539b --- /dev/null +++ b/.changelog/1092.txt @@ -0,0 +1,3 @@ +```release-note:feature +Add preview of vault_radar_source_github_enterprise resource. +``` diff --git a/docs/resources/vault_radar_source_github_enterprise.md b/docs/resources/vault_radar_source_github_enterprise.md new file mode 100644 index 000000000..82eec9207 --- /dev/null +++ b/docs/resources/vault_radar_source_github_enterprise.md @@ -0,0 +1,46 @@ +--- +page_title: "hcp_vault_radar_source_github_enterprise Resource - terraform-provider-hcp" +subcategory: "" +description: |- + This terraform resource manages a GitHub Enterprise Server data source lifecycle in Vault Radar. +--- + +# hcp_vault_radar_source_github_enterprise (Resource) + +-> **Note:** HCP Vault Radar Terraform resources are in preview. + +This terraform resource manages a GitHub Enterprise Server data source lifecycle in Vault Radar. + +## Example Usage + +```terraform +variable "github_enterprise_token" { + type = string + sensitive = true +} + +resource "hcp_vault_radar_source_github_enterprise" "example" { + domain_name = "myserver.acme.com" + github_organization = "my-github-org" + token = var.github_enterprise_token + project_id = "my-project-id" +} +``` + + + +## Schema + +### Required + +- `domain_name` (String) Fully qualified domain name of the server. (Example: myserver.acme.com) +- `github_organization` (String) GitHub organization Vault Radar will monitor. Example: "octocat" for the org https://yourcodeserver.com/octocat +- `token` (String, Sensitive) GitHub personal access token. + +### Optional + +- `project_id` (String) The ID of the HCP project where Vault Radar is located. If not specified, the project specified in the HCP Provider config block will be used, if configured. + +### Read-Only + +- `id` (String) The ID of this resource. diff --git a/examples/resources/hcp_vault_radar_source_github_enterprise/resource.tf b/examples/resources/hcp_vault_radar_source_github_enterprise/resource.tf new file mode 100644 index 000000000..8264c8c9a --- /dev/null +++ b/examples/resources/hcp_vault_radar_source_github_enterprise/resource.tf @@ -0,0 +1,11 @@ +variable "github_enterprise_token" { + type = string + sensitive = true +} + +resource "hcp_vault_radar_source_github_enterprise" "example" { + domain_name = "myserver.acme.com" + github_organization = "my-github-org" + token = var.github_enterprise_token + project_id = "my-project-id" +} \ No newline at end of file diff --git a/internal/clients/client.go b/internal/clients/client.go index 1edefc8f3..e00801bdc 100644 --- a/internal/clients/client.go +++ b/internal/clients/client.go @@ -61,6 +61,9 @@ import ( cloud_webhook "github.com/hashicorp/hcp-sdk-go/clients/cloud-webhook/stable/2023-05-31/client" "github.com/hashicorp/hcp-sdk-go/clients/cloud-webhook/stable/2023-05-31/client/webhook_service" + cloud_vault_radar "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-radar/preview/2023-05-01/client" + radar_src_registration_service "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-radar/preview/2023-05-01/client/data_source_registration_service" + hcpConfig "github.com/hashicorp/hcp-sdk-go/config" sdk "github.com/hashicorp/hcp-sdk-go/httpclient" ) @@ -69,25 +72,26 @@ import ( type Client struct { Config ClientConfig - Billing billing_account_service.ClientService - Boundary boundary_service.ClientService - Consul consul_service.ClientService - IAM iam_service.ClientService - Network network_service.ClientService - Operation operation_service.ClientService - Organization organization_service.ClientService - Packer packer_service.ClientService - PackerV2 packer_service_v2.ClientService - Project project_service.ClientService - ServicePrincipals service_principals_service.ClientService - Groups groups_service.ClientService - Vault vault_service.ClientService - VaultSecrets secret_service.ClientService - VaultSecretsPreview secret_service_preview.ClientService - Waypoint waypoint_service.ClientService - Webhook webhook_service.ClientService - LogService log_service.ClientService - ResourceService resource_service.ClientService + Billing billing_account_service.ClientService + Boundary boundary_service.ClientService + Consul consul_service.ClientService + IAM iam_service.ClientService + Network network_service.ClientService + Operation operation_service.ClientService + Organization organization_service.ClientService + Packer packer_service.ClientService + PackerV2 packer_service_v2.ClientService + Project project_service.ClientService + ServicePrincipals service_principals_service.ClientService + Groups groups_service.ClientService + Vault vault_service.ClientService + VaultSecrets secret_service.ClientService + VaultSecretsPreview secret_service_preview.ClientService + Waypoint waypoint_service.ClientService + Webhook webhook_service.ClientService + LogService log_service.ClientService + ResourceService resource_service.ClientService + RadarSourceRegistrationService radar_src_registration_service.ClientService } // ClientConfig specifies configuration for the client that interacts with HCP @@ -157,26 +161,27 @@ func NewClient(config ClientConfig) (*Client, error) { } client := &Client{ - Config: config, - Billing: cloud_billing.New(httpClient, nil).BillingAccountService, - Boundary: cloud_boundary.New(httpClient, nil).BoundaryService, - Consul: cloud_consul.New(httpClient, nil).ConsulService, - IAM: cloud_iam.New(httpClient, nil).IamService, - Network: cloud_network.New(httpClient, nil).NetworkService, - Operation: cloud_operation.New(httpClient, nil).OperationService, - Organization: cloud_resource_manager.New(httpClient, nil).OrganizationService, - Packer: cloud_packer.New(httpClient, nil).PackerService, - PackerV2: cloud_packer_v2.New(httpClient, nil).PackerService, - Project: cloud_resource_manager.New(httpClient, nil).ProjectService, - ServicePrincipals: cloud_iam.New(httpClient, nil).ServicePrincipalsService, - Groups: cloud_iam.New(httpClient, nil).GroupsService, - Vault: cloud_vault.New(httpClient, nil).VaultService, - VaultSecrets: cloud_vault_secrets.New(httpClient, nil).SecretService, - VaultSecretsPreview: cloud_vault_secrets_preview.New(httpClient, nil).SecretService, - Waypoint: cloud_waypoint.New(httpClient, nil).WaypointService, - LogService: cloud_log_service.New(httpClient, nil).LogService, - Webhook: cloud_webhook.New(httpClient, nil).WebhookService, - ResourceService: cloud_resource_manager.New(httpClient, nil).ResourceService, + Config: config, + Billing: cloud_billing.New(httpClient, nil).BillingAccountService, + Boundary: cloud_boundary.New(httpClient, nil).BoundaryService, + Consul: cloud_consul.New(httpClient, nil).ConsulService, + IAM: cloud_iam.New(httpClient, nil).IamService, + Network: cloud_network.New(httpClient, nil).NetworkService, + Operation: cloud_operation.New(httpClient, nil).OperationService, + Organization: cloud_resource_manager.New(httpClient, nil).OrganizationService, + Packer: cloud_packer.New(httpClient, nil).PackerService, + PackerV2: cloud_packer_v2.New(httpClient, nil).PackerService, + Project: cloud_resource_manager.New(httpClient, nil).ProjectService, + ServicePrincipals: cloud_iam.New(httpClient, nil).ServicePrincipalsService, + Groups: cloud_iam.New(httpClient, nil).GroupsService, + Vault: cloud_vault.New(httpClient, nil).VaultService, + VaultSecrets: cloud_vault_secrets.New(httpClient, nil).SecretService, + VaultSecretsPreview: cloud_vault_secrets_preview.New(httpClient, nil).SecretService, + Waypoint: cloud_waypoint.New(httpClient, nil).WaypointService, + LogService: cloud_log_service.New(httpClient, nil).LogService, + Webhook: cloud_webhook.New(httpClient, nil).WebhookService, + ResourceService: cloud_resource_manager.New(httpClient, nil).ResourceService, + RadarSourceRegistrationService: cloud_vault_radar.New(httpClient, nil).DataSourceRegistrationService, } return client, nil diff --git a/internal/clients/vault_radar.go b/internal/clients/vault_radar.go new file mode 100644 index 000000000..2cc391c3f --- /dev/null +++ b/internal/clients/vault_radar.go @@ -0,0 +1,119 @@ +package clients + +import ( + "context" + "errors" + "time" + + radar_service "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-radar/preview/2023-05-01/client/data_source_registration_service" + + "github.com/hashicorp/terraform-plugin-log/tflog" +) + +func OnboardRadarSource(ctx context.Context, client *Client, projectID string, source radar_service.OnboardDataSourceBody) (*radar_service.OnboardDataSourceOK, error) { + onboardParams := radar_service.NewOnboardDataSourceParams() + onboardParams.Context = ctx + onboardParams.LocationProjectID = projectID + onboardParams.Body = source + + onboardResp, err := client.RadarSourceRegistrationService.OnboardDataSource(onboardParams, nil) + if err != nil { + return nil, err + } + + return onboardResp, nil +} + +func GetRadarSource(ctx context.Context, client *Client, projectID, sourceID string) (*radar_service.GetDataSourceByIDOK, error) { + getParams := radar_service.NewGetDataSourceByIDParams() + getParams.Context = ctx + getParams.ID = sourceID + getParams.LocationProjectID = projectID + + getResp, err := client.RadarSourceRegistrationService.GetDataSourceByID(getParams, nil) + if err != nil { + return nil, err + } + + return getResp, nil +} + +func OffboardRadarSource(ctx context.Context, client *Client, projectID, sourceID string) error { + tflog.SetField(ctx, "radar_source_id", sourceID) + + deleteParams := radar_service.NewOffboardDataSourceParams() + deleteParams.Context = ctx + deleteParams.LocationProjectID = projectID + deleteParams.Body = radar_service.OffboardDataSourceBody{ + ID: sourceID, + } + + tflog.Trace(ctx, "Initiate radar source offboarding.") + if _, err := client.RadarSourceRegistrationService.OffboardDataSource(deleteParams, nil); err != nil { + return err + } + + return WaitOnOffboardRadarSource(ctx, client, projectID, sourceID) +} + +func WaitOnOffboardRadarSource(ctx context.Context, client *Client, projectID, sourceID string) error { + deletionConfirmation := func() (bool, error) { + tflog.Trace(ctx, "Confirming radar source deletion.") + if _, err := GetRadarSource(ctx, client, projectID, sourceID); err != nil { + if IsResponseCodeNotFound(err) { + // success, resource not found. + tflog.Trace(ctx, "Success, radar source deletion confirmed.") + return true, nil + } + + tflog.Error(ctx, "Failed to confirm radar source deletion.") + return false, err + } + + // Resource still exists. + return false, nil + } + + retry := 10 * time.Second + timeout := 10 * time.Minute + maxConsecutiveErrors := 5 + return waitFor(ctx, retry, timeout, maxConsecutiveErrors, deletionConfirmation) +} + +// waitFor waits for isDone to return true or retrying every retry duration until timeout. +// Returns an error if isDone errors or timeout expires. +func waitFor(ctx context.Context, retry, timeout time.Duration, maxConsecutiveErrors int, isDone func() (bool, error)) error { + consecutiveErrors := 0 + + waitCtx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + ticker := time.NewTicker(retry) + defer ticker.Stop() + + for { + if done, err := isDone(); err != nil { + // Check for consecutiveErrors and return error if it exceeds the limit. + if consecutiveErrors >= maxConsecutiveErrors { + return errors.New("max consecutive errors reached") + } + consecutiveErrors++ + // Don't call continue here, as we want to wait for the next retry duration. + } else if done { + return nil + } else { + // done == false, err == nil + // Reset consecutiveErrors for next retry call to isDone. + consecutiveErrors = 0 + } + + select { + case <-ticker.C: + // retry duration has passed. + case <-waitCtx.Done(): + return errors.New("timeout expired while waiting") + case <-ctx.Done(): + return errors.New("context canceled while waiting") + } + } +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 1e8a2a510..62e47ec99 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -26,6 +26,7 @@ import ( "github.com/hashicorp/terraform-provider-hcp/internal/provider/logstreaming" "github.com/hashicorp/terraform-provider-hcp/internal/provider/packer" "github.com/hashicorp/terraform-provider-hcp/internal/provider/resourcemanager" + "github.com/hashicorp/terraform-provider-hcp/internal/provider/vaultradar" "github.com/hashicorp/terraform-provider-hcp/internal/provider/vaultsecrets" "github.com/hashicorp/terraform-provider-hcp/internal/provider/waypoint" "github.com/hashicorp/terraform-provider-hcp/internal/provider/webhook" @@ -178,6 +179,8 @@ func (p *ProviderFramework) Resources(ctx context.Context) []func() resource.Res waypoint.NewAddOnResource, waypoint.NewAddOnDefinitionResource, waypoint.NewTfcConfigResource, + // Radar + vaultradar.NewSourceGitHubEnterpriseResource, }, packer.ResourceSchemaBuilders...) } diff --git a/internal/provider/vaultradar/resource_radar_source_github_enterprise.go b/internal/provider/vaultradar/resource_radar_source_github_enterprise.go new file mode 100644 index 000000000..3a9fe86c3 --- /dev/null +++ b/internal/provider/vaultradar/resource_radar_source_github_enterprise.go @@ -0,0 +1,240 @@ +package vaultradar + +import ( + "context" + "fmt" + "regexp" + + radar_service "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-radar/preview/2023-05-01/client/data_source_registration_service" + + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-provider-hcp/internal/clients" + "github.com/hashicorp/terraform-provider-hcp/internal/provider/modifiers" +) + +var ( + _ resource.Resource = &sourceGitHubEnterpriseResource{} + _ resource.ResourceWithConfigure = &sourceGitHubEnterpriseResource{} +) + +func NewSourceGitHubEnterpriseResource() resource.Resource { + return &sourceGitHubEnterpriseResource{} +} + +type sourceGitHubEnterpriseResource struct { + client *clients.Client +} + +func (r *sourceGitHubEnterpriseResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vault_radar_source_github_enterprise" +} + +func (r *sourceGitHubEnterpriseResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "This terraform resource manages a GitHub Enterprise Server data source lifecycle in Vault Radar.", + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + Description: "The ID of this resource.", + }, + "domain_name": schema.StringAttribute{ + Description: "Fully qualified domain name of the server. (Example: myserver.acme.com)", + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile(`^(?:[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*\.)+[a-zA-Z]{2,}$`), + "must be a valid domain name", + ), + }, + }, + "github_organization": schema.StringAttribute{ + Description: `GitHub organization Vault Radar will monitor. Example: "octocat" for the org https://yourcodeserver.com/octocat`, + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile(`^[a-zA-Z0-9-_.]+$`), + "must contain only letters, numbers, hyphens, underscores, or periods", + ), + }, + }, + "token": schema.StringAttribute{ + Description: "GitHub personal access token.", + Required: true, + Sensitive: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + // Optional inputs + "project_id": schema.StringAttribute{ + Description: "The ID of the HCP project where Vault Radar is located. If not specified, the project specified in the HCP Provider config block will be used, if configured.", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + stringplanmodifier.UseStateForUnknown(), + }, + }, + }, + } +} + +type gitHubEnterpriseSource struct { + ID types.String `tfsdk:"id"` + DomainName types.String `tfsdk:"domain_name"` + GitHubOrganization types.String `tfsdk:"github_organization"` + Token types.String `tfsdk:"token"` + ProjectID types.String `tfsdk:"project_id"` +} + +func (r *sourceGitHubEnterpriseResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*clients.Client) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected *clients.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + return + } + r.client = client +} + +func (r *sourceGitHubEnterpriseResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + modifiers.ModifyPlanForDefaultProjectChange(ctx, r.client.Config.ProjectID, req.State, req.Config, req.Plan, resp) +} + +func (r *sourceGitHubEnterpriseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan gitHubEnterpriseSource + + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + projectID := r.client.Config.ProjectID + if !plan.ProjectID.IsUnknown() { + projectID = plan.ProjectID.ValueString() + } + + res, err := clients.OnboardRadarSource(ctx, r.client, projectID, radar_service.OnboardDataSourceBody{ + Type: "github_enterprise", + Name: plan.GitHubOrganization.ValueString(), + ConnectionURL: plan.DomainName.ValueString(), + Token: plan.Token.ValueString(), + }) + if err != nil { + resp.Diagnostics.AddError("Error creating Radar source", err.Error()) + return + } + + plan.ID = types.StringValue(res.GetPayload().ID) + plan.ProjectID = types.StringValue(projectID) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + + tflog.Trace(ctx, "Created Radar resource") +} + +func (r *sourceGitHubEnterpriseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state gitHubEnterpriseSource + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + projectID := r.client.Config.ProjectID + if !state.ProjectID.IsUnknown() { + projectID = state.ProjectID.ValueString() + } + + res, err := clients.GetRadarSource(ctx, r.client, projectID, state.ID.ValueString()) + if err != nil { + if clients.IsResponseCodeNotFound(err) { + // Resource is no longer on the server. + tflog.Info(ctx, "Radar source not found, removing from state.") + resp.State.RemoveResource(ctx) + return + } + resp.Diagnostics.AddError("Unable to get Radar source", err.Error()) + return + } + + // Resource is marked as deleted on the server. + if res.GetPayload().Deleted { + // Don't update or remove the state, because its has not been fully deleted server side. + tflog.Warn(ctx, "Radar source marked for deletion.") + return + } + + // The only other state that could change related to this resource is the token, and for obvious reasons we don't + // return that in the read response. So we don't need to update the state here. + tflog.Trace(ctx, "Read Radar resource") +} + +func (r *sourceGitHubEnterpriseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state gitHubEnterpriseSource + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + projectID := r.client.Config.ProjectID + if !state.ProjectID.IsUnknown() { + projectID = state.ProjectID.ValueString() + } + + // Assert resource still exists. + res, err := clients.GetRadarSource(ctx, r.client, projectID, state.ID.ValueString()) + if err != nil { + if clients.IsResponseCodeNotFound(err) { + // Resource is no longer on the server. + tflog.Info(ctx, "Radar source not found, removing from state.") + resp.State.RemoveResource(ctx) + return + } + resp.Diagnostics.AddError("Unable to get Radar source", err.Error()) + return + } + + // Resource is already marked as being deleted on the server. Wait for it to be fully deleted. + if res.GetPayload().Deleted { + tflog.Info(ctx, "Radar resource already marked for deletion, waiting for full deletion") + if err := clients.WaitOnOffboardRadarSource(ctx, r.client, projectID, state.ID.ValueString()); err != nil { + resp.Diagnostics.AddError("Unable to delete Radar source", err.Error()) + return + } + + tflog.Trace(ctx, "Deleted Radar resource") + return + } + + // Offboard the Radar source. + if err := clients.OffboardRadarSource(ctx, r.client, projectID, state.ID.ValueString()); err != nil { + resp.Diagnostics.AddError("Unable to delete Radar source", err.Error()) + return + } + + tflog.Trace(ctx, "Deleted Radar resource") +} + +func (r *sourceGitHubEnterpriseResource) Update(_ context.Context, _ resource.UpdateRequest, resp *resource.UpdateResponse) { + // In-place update is not supported. + // Plans to support updating the token will be in a future iteration. + resp.Diagnostics.AddError("Unexpected provider error", "This is an internal error, please report this issue to the provider developers") +} diff --git a/internal/provider/vaultradar/resource_radar_source_github_enterprise_test.go b/internal/provider/vaultradar/resource_radar_source_github_enterprise_test.go new file mode 100644 index 000000000..5e01911fd --- /dev/null +++ b/internal/provider/vaultradar/resource_radar_source_github_enterprise_test.go @@ -0,0 +1,50 @@ +package vaultradar_test + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-provider-hcp/internal/provider/acctest" +) + +func TestRadarSourceGitHubEnterprise(t *testing.T) { + // Requires Project already setup with Radar. + // Requires a Service Account with an Admin role on the Project. + // Requires access to a GitHub Enterprise Server self-managed instance. + // Requires the following environment variables to be set: + projectID := os.Getenv("HCP_PROJECT_ID") + githubOrganization := os.Getenv("RADAR_GITHUB_ENTERPRISE_ORGANIZATION") + domainName := os.Getenv("RADAR_GITHUB_ENTERPRISE_DOMAIN") + token := os.Getenv("RADAR_GITHUB_ENTERPRISE_TOKEN") + + if projectID == "" || githubOrganization == "" || domainName == "" || token == "" { + t.Skip("HCP_PROJECT_ID, RADAR_GITHUB_ENTERPRISE_ORGANIZATION, RADAR_GITHUB_ENTERPRISE_DOMAIN, and RADAR_GITHUB_ENTERPRISE_TOKEN must be set for acceptance tests") + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // CREATE + { + Config: fmt.Sprintf(` + resource "hcp_vault_radar_source_github_enterprise" "example" { + project_id = %q + github_organization = %q + domain_name = %q + token = %q + } + `, projectID, githubOrganization, domainName, token), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("hcp_vault_radar_source_github_enterprise.example", "project_id", projectID), + resource.TestCheckResourceAttr("hcp_vault_radar_source_github_enterprise.example", "github_organization", githubOrganization), + resource.TestCheckResourceAttr("hcp_vault_radar_source_github_enterprise.example", "domain_name", domainName), + resource.TestCheckResourceAttrSet("hcp_vault_radar_source_github_enterprise.example", "id"), + ), + }, + // UPDATE not supported at this time. + // DELETE happens automatically. + }, + }) +} diff --git a/templates/resources/vault_radar_source_github_enterprise.md.tmpl b/templates/resources/vault_radar_source_github_enterprise.md.tmpl new file mode 100644 index 000000000..c764e46a7 --- /dev/null +++ b/templates/resources/vault_radar_source_github_enterprise.md.tmpl @@ -0,0 +1,19 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +-> **Note:** HCP Vault Radar Terraform resources are in preview. + +{{ .Description | trimspace }} + +## Example Usage + +{{ tffile "examples/resources/hcp_vault_radar_source_github_enterprise/resource.tf" }} + + +{{ .SchemaMarkdown | trimspace }}