Skip to content
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

custom_domain_verify resource and add retries to custom_domains #15

Merged
merged 9 commits into from
Nov 7, 2024
4 changes: 2 additions & 2 deletions docs/resources/certificate_signing_request_export.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Resource to create and manage a certificate signing request.
- `algorithm` (String) The algorithm for the private key. The encryption algorithm will either be RSA-2048 or ECDSA P-256 depending on the algorithm choice. The default is `rsa`. Supported values are `rsa`, `ecdsa`.
- `business_category` (String) Category of business, such as "Private Organization", “Government Entity”, “Business Entity”, or “Non-Commercial Entity”. Relevant for EV certificates.
- `city` (String) City for the CSR
- `common_name` (String) Domain name that the SSL certificate is securing
- `common_name` (String) Domain name that the SSL certificate is securing. At least one of `common_name` or `subject_alternative_names` must be specified.
- `country` (String) Two-letter ISO-3166 country code
- `email` (String) Email for the CSR
- `jurisdiction_city` (String) This field contains only information relevant to the Jurisdiction of Incorporation or Registration. Relevant for EV certificates.
Expand All @@ -32,7 +32,7 @@ Resource to create and manage a certificate signing request.
- `serial_number` (String) The Registration (or similar) Number assigned to the Subject by the Incorporating or Registration Agency in its Jurisdiction of Incorporation or Registration. Relevant for EV certificates.
- `state` (String) State for the CSR
- `street_address` (String) Street address for the CSR
- `subject_alternative_names` (Set of String) Additional domain or domains that the SSL certificate is securing.
- `subject_alternative_names` (Set of String) Additional domain or domains that the SSL certificate is securing. At least one of `common_name` or `subject_alternative_names` must be specified.

### Read-Only

Expand Down
2 changes: 1 addition & 1 deletion docs/resources/cookie_domains.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ resource "identitycloud_cookie_domains" "example" {

Import is supported using the following syntax:

~> This resource is singleton, so the value of "id" doesn't matter - it is just a placeholder, and required by Terraform
~> realm_id should be either `alpha` or `bravo`.

```shell
terraform import identitycloud_cookie_domains.example id
Expand Down
41 changes: 41 additions & 0 deletions docs/resources/custom_domain_verify.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
page_title: "identitycloud_custom_domain_verify Resource - terraform-provider-identitycloud"
subcategory: ""
description: |-
Resource to verify a custom domain.
---

# identitycloud_custom_domain_verify (Resource)

Resource to verify a custom domain.

Any custom domains will be validated by AIC when set. CNAME record verification can also be deactivated by submitting a ticket. See [the documentation on custom domains](https://backstage.forgerock.com/docs/idcloud/latest/realms/custom-domains.html) for more information.

## Example Usage

```terraform
resource "identitycloud_custom_domain_verify" "example" {
name = "mydomain.example.com"
timeouts = {
create = "30m"
}
}
```

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

### Required

- `name` (String) The canonical name of the domain to be validated.

### Optional

- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts))

<a id="nestedatt--timeouts"></a>
### Nested Schema for `timeouts`

Optional:

- `create` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m", as a time to wait for DNS record changes to propagate for verification. Valid time units are "s" (seconds), "m" (minutes), "h" (hours). The default is 1 minute.
15 changes: 13 additions & 2 deletions docs/resources/custom_domains.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ description: |-

Resource to create and manage the custom domains.

Any custom domains will be validated by AIC when set. CNAME record verification can also be deactivated by submitting a ticket. See [the documentation on custom domains](https://backstage.forgerock.com/docs/idcloud/latest/realms/custom-domains.html) for more information.

## Example Usage

```terraform
resource "identitycloud_custom_domains" "example" {
realm = "alpha"
domains = ["mydomain1", "mydomain2"]
domains = ["mydomain.example.com", "mydomain2.example.com"]
}
```

Expand All @@ -25,12 +27,21 @@ resource "identitycloud_custom_domains" "example" {

- `domains` (Set of String) The custom domains. Defaults to an empty set.
- `realm` (String) Realm for the domain. Supported values are `alpha`, `bravo`.
- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts))

<a id="nestedatt--timeouts"></a>
### Nested Schema for `timeouts`

Optional:

- `create` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m", as a time to wait for DNS record changes to propagate for verification on initial create of the resource. Valid time units are "s" (seconds), "m" (minutes), "h" (hours). The default is 1 minute.
- `update` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m", as a time to wait for DNS record changes to propagate for verification on update of the resource. Valid time units are "s" (seconds), "m" (minutes), "h" (hours). The default is 1 minute.

## Import

Import is supported using the following syntax:

~> This resource is singleton, so the value of "id" doesn't matter - it is just a placeholder, and required by Terraform
~> realm_id should be either `alpha` or `bravo`.

```shell
terraform import identitycloud_custom_domains.example realm_id
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
resource "identitycloud_custom_domain_verify" "example" {
name = "mydomain.example.com"
timeouts = {
create = "30m"
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
resource "identitycloud_custom_domains" "example" {
realm = "alpha"
domains = ["mydomain1", "mydomain2"]
domains = ["mydomain.example.com", "mydomain2.example.com"]
}
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ require (
github.com/golangci/golangci-lint v1.61.0
github.com/hashicorp/terraform-plugin-docs v0.19.3
github.com/hashicorp/terraform-plugin-framework v1.11.0
github.com/hashicorp/terraform-plugin-framework-timeouts v0.4.1
github.com/hashicorp/terraform-plugin-framework-validators v0.12.0
github.com/hashicorp/terraform-plugin-go v0.23.0
github.com/hashicorp/terraform-plugin-log v0.9.0
github.com/hashicorp/terraform-plugin-testing v1.10.0
github.com/katbyte/terrafmt v0.5.3
github.com/pavius/impi v0.0.3
github.com/pingidentity/identitycloud-go-client v0.2.0
github.com/sethvargo/go-retry v0.3.0
github.com/terraform-linters/tflint v0.51.1
)

Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,8 @@ github.com/hashicorp/terraform-plugin-docs v0.19.3 h1:xoxpeIuBfnoGxXY0dTajdj4GjE
github.com/hashicorp/terraform-plugin-docs v0.19.3/go.mod h1:4pLASsatTmRynVzsjEhbXZ6s7xBlUw/2Kt0zfrq8HxA=
github.com/hashicorp/terraform-plugin-framework v1.11.0 h1:M7+9zBArexHFXDx/pKTxjE6n/2UCXY6b8FIq9ZYhwfE=
github.com/hashicorp/terraform-plugin-framework v1.11.0/go.mod h1:qBXLDn69kM97NNVi/MQ9qgd1uWWsVftGSnygYG1tImM=
github.com/hashicorp/terraform-plugin-framework-timeouts v0.4.1 h1:gm5b1kHgFFhaKFhm4h2TgvMUlNzFAtUqlcOWnWPm+9E=
github.com/hashicorp/terraform-plugin-framework-timeouts v0.4.1/go.mod h1:MsjL1sQ9L7wGwzJ5RjcI6FzEMdyoBnw+XK8ZnOvQOLY=
github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 h1:HOjBuMbOEzl7snOdOoUfE2Jgeto6JOjLVQ39Ls2nksc=
github.com/hashicorp/terraform-plugin-framework-validators v0.12.0/go.mod h1:jfHGE/gzjxYz6XoUwi/aYiiKrJDeutQNUtGQXkaHklg=
github.com/hashicorp/terraform-plugin-go v0.23.0 h1:AALVuU1gD1kPb48aPQUjug9Ir/125t+AAurhqphJ2Co=
Expand Down Expand Up @@ -883,6 +885,8 @@ github.com/securego/gosec/v2 v2.21.2 h1:deZp5zmYf3TWwU7A7cR2+SolbTpZ3HQiwFqnzQyE
github.com/securego/gosec/v2 v2.21.2/go.mod h1:au33kg78rNseF5PwPnTWhuYBFf534bvJRvOrgZ/bFzU=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE=
github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas=
github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c h1:W65qqJCIOVP4jpqPQ0YvHYKwcMEMVWIzWC5iNQQfBTU=
github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
Expand Down
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ func (p *identityCloudProvider) Resources(_ context.Context) []func() resource.R
cookiedomains.CookieDomainsResource,
csrs.CertificateSigningRequestResource,
customdomains.CustomDomainsResource,
customdomains.CustomDomainVerifyResource,
secrets.SecretResource,
variable.VariableResource,
}
Expand Down
59 changes: 43 additions & 16 deletions internal/providererror/resourceerror.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,29 @@ func AddResourceNotFoundWarning(ctx context.Context, diagnostics *diag.Diagnosti
}
}

type aicErrorResponse struct {
type AicErrorResponse struct {
Code int `json:"code"`
Message string `json:"message"`
}

func ReadErrorResponse(ctx context.Context, httpResp *http.Response) (*AicErrorResponse, []byte) {
if httpResp == nil {
return nil, nil
}
var aicError AicErrorResponse

defer httpResp.Body.Close()
body, err := io.ReadAll(httpResp.Body)
if err == nil {
err = json.Unmarshal(body, &aicError)
if err == nil {
return &aicError, body
}
return nil, body
}
return nil, nil
}

// Report an HTTP error
func ReportHttpError(ctx context.Context, diagnostics *diag.Diagnostics, errorSummary string, err error, httpResp *http.Response) {
httpErrorPrinted := false
Expand All @@ -39,21 +57,7 @@ func ReportHttpError(ctx context.Context, diagnostics *diag.Diagnostics, errorSu
if httpResp != nil {
body, internalError = io.ReadAll(httpResp.Body)
if internalError == nil {
tflog.Debug(ctx, "Error HTTP response body: "+string(body))
var aicError aicErrorResponse
internalError = json.Unmarshal(body, &aicError)
if internalError == nil {
var errorDetail strings.Builder
errorDetail.WriteString("Error summary: ")
errorDetail.WriteString(errorSummary)
errorDetail.WriteString("\nMessage: ")
errorDetail.WriteString(aicError.Message)
errorDetail.WriteString("\nCode: ")
errorDetail.WriteString(strconv.Itoa(aicError.Code))
diagnostics.AddError(AicAPIError, errorDetail.String())
} else {
diagnostics.AddError(AicAPIError, errorSummary+"\n"+err.Error()+" - Detail:\n"+string(body))
}
ReportHttpErrorBody(ctx, diagnostics, errorSummary, err, body)
httpErrorPrinted = true
}
}
Expand All @@ -64,3 +68,26 @@ func ReportHttpError(ctx context.Context, diagnostics *diag.Diagnostics, errorSu
diagnostics.AddError(AicAPIError, errorSummary+"\n"+err.Error())
}
}

// Report an HTTP error
func ReportHttpErrorBody(ctx context.Context, diagnostics *diag.Diagnostics, errorSummary string, err error, httpRespBody []byte) {
if httpRespBody == nil {
diagnostics.AddError(AicAPIError, errorSummary+"\n"+err.Error())
} else {
tflog.Debug(ctx, "Error HTTP response body: "+string(httpRespBody))
var aicError AicErrorResponse
internalError := json.Unmarshal(httpRespBody, &aicError)
if internalError == nil {
var errorDetail strings.Builder
errorDetail.WriteString("Error summary: ")
errorDetail.WriteString(errorSummary)
errorDetail.WriteString("\nMessage: ")
errorDetail.WriteString(aicError.Message)
errorDetail.WriteString("\nCode: ")
errorDetail.WriteString(strconv.Itoa(aicError.Code))
diagnostics.AddError(AicAPIError, errorDetail.String())
} else {
diagnostics.AddError(AicAPIError, errorSummary+"\n"+err.Error()+" - Detail:\n"+string(httpRespBody))
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading