Skip to content

Commit

Permalink
Add support for SSH install: iam-write (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
GGonryun authored Apr 18, 2024
1 parent 363a1f3 commit 322c674
Show file tree
Hide file tree
Showing 13 changed files with 589 additions and 40 deletions.
6 changes: 3 additions & 3 deletions docs/resources/aws_iam_write.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,9 @@ resource "p0_aws_iam_write" "example" {
### Read-Only

- `state` (String) This account's install progress in the P0 application:
- 'stage': The account has been staged for installation
- 'configure': The account is available to be added to P0, and may be configured
- 'installed': The account is fully installed
- 'stage': The account has been staged for installation
- 'configure': The account is available to be added to P0, and may be configured
- 'installed': The account is fully installed

<a id="nestedatt--login"></a>
### Nested Schema for `login`
Expand Down
42 changes: 42 additions & 0 deletions docs/resources/ssh_aws.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "p0_ssh_aws Resource - p0"
subcategory: ""
description: |-
An AWS SSH Installation.
Installing SSH allows you to manage access to your servers on AWS.
---

# p0_ssh_aws (Resource)

An AWS SSH Installation.

Installing SSH allows you to manage access to your servers on AWS.

## Example Usage

```terraform
resource "p0_ssh_aws" "example" {
account_id = "123456789012"
group_key = "vegetables"
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `account_id` (String) The AWS account ID

### Optional

- `group_key` (String) If present, AWS instances will be grouped by the value of this tag. Access can be requested, in one request, to all instances with a shared tag value
- `label` (String) The AWS account's alias (if available)

### Read-Only

- `state` (String) This account's install progress in the P0 application:
- 'stage': The account has been staged for installation
- 'configure': The account is available to be added to P0, and may be configured
- 'installed': The account is fully installed
37 changes: 37 additions & 0 deletions docs/resources/ssh_gcp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "p0_ssh_gcp Resource - p0"
subcategory: ""
description: |-
A Google Cloud SSH installation.
Installing SSH allows you to manage access to your servers on Google Cloud.
---

# p0_ssh_gcp (Resource)

A Google Cloud SSH installation.

Installing SSH allows you to manage access to your servers on Google Cloud.

## Example Usage

```terraform
resource "p0_ssh_gcp" "example" {
project_id = "my-project"
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `project_id` (String) The Google Cloud project ID

### Read-Only

- `label` (String) The Google Cloud project's alias (if available)
- `state` (String) This account's install progress in the P0 application:
- 'stage': The account has been staged for installation
- 'configure': The account is available to be added to P0, and may be configured
- 'installed': The account is fully installed
4 changes: 4 additions & 0 deletions examples/resources/p0_ssh_aws/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
resource "p0_ssh_aws" "example" {
account_id = "123456789012"
group_key = "vegetables"
}
3 changes: 3 additions & 0 deletions examples/resources/p0_ssh_gcp/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
resource "p0_ssh_gcp" "example" {
project_id = "my-project"
}
3 changes: 3 additions & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/p0-security/terraform-provider-p0/internal"
"github.com/p0-security/terraform-provider-p0/internal/provider/resources"
installaws "github.com/p0-security/terraform-provider-p0/internal/provider/resources/install/aws"
installssh "github.com/p0-security/terraform-provider-p0/internal/provider/resources/install/ssh"
)

// Ensure P0Provider satisfies various provider interfaces.
Expand Down Expand Up @@ -105,6 +106,8 @@ func (p *P0Provider) Resources(ctx context.Context) []func() resource.Resource {
resources.NewRoutingRules,
installaws.NewStagedAws,
installaws.NewAwsIamWrite,
installssh.NewSshAwsIamWrite,
installssh.NewSshGcpIamWrite,
}
}

Expand Down
16 changes: 11 additions & 5 deletions internal/provider/resources/install/aws/common.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
package installaws

import "regexp"
import (
"regexp"

var Aws = "aws"
installresources "github.com/p0-security/terraform-provider-p0/internal/provider/resources/install"
)

var IamWrite = "iam-write"
var Inventory = "inventory"
const (
Aws = "aws"
Inventory = "inventory"
)

// All installable AWS components.
var Components = []string{IamWrite, Inventory}
var Components = []string{installresources.IamWrite, Inventory}

var AwsAccountIdRegex = regexp.MustCompile(`^\d{12}$`)
var AwsIdpPattern = regexp.MustCompile(`^[\w.-/]+$`)
var OktaAppIdRegex = regexp.MustCompile(`^0o\w+$`)

const AwsLabelMarkdownDescription = "The AWS account's alias (if available)"
15 changes: 6 additions & 9 deletions internal/provider/resources/install/aws/iam_write.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,8 @@ resource "p0_aws_iam_write" "installed_account" {
MarkdownDescription: `The AWS account's alias (if available)`,
},
"state": schema.StringAttribute{
Computed: true,
MarkdownDescription: `This account's install progress in the P0 application:
- 'stage': The account has been staged for installation
- 'configure': The account is available to be added to P0, and may be configured
- 'installed': The account is fully installed`,
Computed: true,
MarkdownDescription: installresources.StateMarkdownDescription,
},
"login": schema.SingleNestedAttribute{
Required: true,
Expand Down Expand Up @@ -363,8 +360,8 @@ func (r *AwsIamWrite) toJson(data any) any {
func (r *AwsIamWrite) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
providerData := internal.Configure(&req, resp)
r.installer = &installresources.Install{
Component: IamWrite,
Integration: Aws,
Component: installresources.IamWrite,
ProviderData: providerData,
GetId: r.getId,
GetItemJson: r.getItemJson,
Expand All @@ -376,7 +373,7 @@ func (r *AwsIamWrite) Configure(ctx context.Context, req resource.ConfigureReque
func (r *AwsIamWrite) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var json awsIamWriteApi
var data awsIamWriteModel
r.installer.Upsert(ctx, &resp.Diagnostics, &req.Plan, &resp.State, &json, &data)
r.installer.UpsertFromStage(ctx, &resp.Diagnostics, &req.Plan, &resp.State, &json, &data)
}

func (r *AwsIamWrite) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
Expand All @@ -388,12 +385,12 @@ func (r *AwsIamWrite) Read(ctx context.Context, req resource.ReadRequest, resp *
func (r *AwsIamWrite) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var json awsIamWriteApi
var data awsIamWriteModel
r.installer.Upsert(ctx, &resp.Diagnostics, &req.Plan, &resp.State, &json, &data)
r.installer.UpsertFromStage(ctx, &resp.Diagnostics, &req.Plan, &resp.State, &json, &data)
}

func (r *AwsIamWrite) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var data awsIamWriteModel
r.installer.Delete(ctx, &resp.Diagnostics, &resp.State, &data)
r.installer.Rollback(ctx, &resp.Diagnostics, &resp.State, &data)
}

func (r *AwsIamWrite) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
Expand Down
28 changes: 25 additions & 3 deletions internal/provider/resources/install/aws/staged.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"context"
"fmt"
"slices"
"strings"

"github.com/hashicorp/terraform-plugin-framework-validators/setvalidator"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
Expand All @@ -18,6 +19,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/p0-security/terraform-provider-p0/internal"
installresources "github.com/p0-security/terraform-provider-p0/internal/provider/resources/install"
)

// Ensure provider defined types fully satisfy framework interfaces.
Expand Down Expand Up @@ -78,7 +80,7 @@ func (r *StagedAws) Schema(ctx context.Context, req resource.SchemaRequest, resp
},
"label": schema.StringAttribute{
Computed: true,
MarkdownDescription: `The AWS account's alias (if available)`,
MarkdownDescription: AwsLabelMarkdownDescription,
},
"service_account_id": schema.StringAttribute{
Computed: true,
Expand Down Expand Up @@ -107,6 +109,10 @@ func (r *StagedAws) Configure(ctx context.Context, req resource.ConfigureRequest
}
}

func (r *StagedAws) itemBasePath() string {
return "integrations/aws/config"
}

func (r *StagedAws) itemPath(component string, id string) string {
return fmt.Sprintf("integrations/aws/config/%s/%s", component, id)
}
Expand All @@ -130,7 +136,7 @@ func (r *StagedAws) fromJson(data *stagedAwsModel, json *stagedAwsJson) {

iamWriteJson, okIamWrite := json.Config.IamWrite[data.Id]
if okIamWrite {
r.fromComponentJson(data, &iamWriteJson, IamWrite)
r.fromComponentJson(data, &iamWriteJson, installresources.IamWrite)
}

inventoryJson, okInventory := json.Config.Inventory[data.Id]
Expand All @@ -141,7 +147,7 @@ func (r *StagedAws) fromJson(data *stagedAwsModel, json *stagedAwsJson) {

func (r *StagedAws) readState(ctx context.Context, diags *diag.Diagnostics, data *stagedAwsModel, state *tfsdk.State) {
var config stagedAwsJson
httpErr := r.data.Get("integrations/aws/config", &config)
httpErr := r.data.Get(r.itemBasePath(), &config)
if httpErr != nil {
diags.AddError("Error communicationg with P0", fmt.Sprintf("Unable to read AWS configuration, got error:\n%s", httpErr))
return
Expand Down Expand Up @@ -187,6 +193,22 @@ func (r *StagedAws) put(ctx context.Context, diags *diag.Diagnostics, plan *tfsd
}

func (r *StagedAws) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
diags := &resp.Diagnostics
diags.Append(req.Plan.Get(ctx, &stagedAwsModel{})...)
if diags.HasError() {
return
}

throwaway_response := struct{}{}
err := r.data.Post(r.itemBasePath(), struct{}{}, &throwaway_response)
if err != nil {
// we can safely 409 Conflict errors, because they indicate the item is already installed
if !strings.Contains(err.Error(), "409 Conflict") {
diags.AddError("Error communicating with P0", fmt.Sprintf("Failed to install integration %s, got error %s", Aws, err))
return
}
}

r.put(ctx, &resp.Diagnostics, &req.Plan, &resp.State, "create")
}

Expand Down
Loading

0 comments on commit 322c674

Please sign in to comment.