Compare commits

...

6 Commits

Author SHA1 Message Date
kjubybot
1bf17c23c8 updated id computation for pfw 2022-02-07 12:14:16 +03:00
kjubybot
668d57cd3b updated docs for pfw and kvmvm 2022-02-07 11:50:42 +03:00
kjubybot
dfeb9a9165 compute os_users parsing fix; driver field fix in compute datasource 2022-02-02 16:14:32 +03:00
kjubybot
ab070b73cb docs and timeouts for pfw 2022-01-31 11:32:25 +03:00
kjubybot
80a4b70db8 added port forwarding resource 2022-01-27 17:51:44 +03:00
kjubybot
28db919ffa Fixed JSON parsing 2022-01-14 18:04:31 +03:00
9 changed files with 378 additions and 87 deletions

View File

@@ -25,7 +25,6 @@ Visit https://github.com/rudecs/terraform-provider-decort for full source code p
package decort package decort
import ( import (
"bytes" "bytes"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
@@ -34,6 +33,7 @@ import (
"net/url" "net/url"
"strconv" "strconv"
"strings" "strings"
// "time" // "time"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@@ -42,30 +42,29 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/helper/schema"
// "github.com/hashicorp/terraform-plugin-sdk/terraform" // "github.com/hashicorp/terraform-plugin-sdk/terraform"
) )
// enumerated constants that define authentication modes // enumerated constants that define authentication modes
const ( const (
MODE_UNDEF = iota // this is the invalid mode - it should never be seen MODE_UNDEF = iota // this is the invalid mode - it should never be seen
MODE_LEGACY = iota MODE_LEGACY = iota
MODE_OAUTH2 = iota MODE_OAUTH2 = iota
MODE_JWT = iota MODE_JWT = iota
) )
type ControllerCfg struct { type ControllerCfg struct {
controller_url string // always required controller_url string // always required
auth_mode_code int // always required auth_mode_code int // always required
auth_mode_txt string // always required, it is a text representation of auth mode auth_mode_txt string // always required, it is a text representation of auth mode
legacy_user string // required for legacy mode legacy_user string // required for legacy mode
legacy_password string // required for legacy mode legacy_password string // required for legacy mode
legacy_sid string // obtained from DECORT controller on successful login in legacy mode legacy_sid string // obtained from DECORT controller on successful login in legacy mode
jwt string // obtained from Outh2 provider on successful login in oauth2 mode, required in jwt mode jwt string // obtained from Outh2 provider on successful login in oauth2 mode, required in jwt mode
app_id string // required for oauth2 mode app_id string // required for oauth2 mode
app_secret string // required for oauth2 mode app_secret string // required for oauth2 mode
oauth2_url string // always required oauth2_url string // always required
decort_username string // assigned to either legacy_user (legacy mode) or Oauth2 user (oauth2 mode) upon successful verification decort_username string // assigned to either legacy_user (legacy mode) or Oauth2 user (oauth2 mode) upon successful verification
cc_client *http.Client // assigned when all initial checks successfully passed cc_client *http.Client // assigned when all initial checks successfully passed
} }
func ControllerConfigure(d *schema.ResourceData) (*ControllerCfg, error) { func ControllerConfigure(d *schema.ResourceData) (*ControllerCfg, error) {
@@ -90,7 +89,7 @@ func ControllerConfigure(d *schema.ResourceData) (*ControllerCfg, error) {
app_id: d.Get("app_id").(string), app_id: d.Get("app_id").(string),
app_secret: d.Get("app_secret").(string), app_secret: d.Get("app_secret").(string),
oauth2_url: d.Get("oauth2_url").(string), oauth2_url: d.Get("oauth2_url").(string),
decort_username: "", decort_username: "",
} }
var allow_unverified_ssl bool var allow_unverified_ssl bool
@@ -137,10 +136,10 @@ func ControllerConfigure(d *schema.ResourceData) (*ControllerCfg, error) {
if allow_unverified_ssl { if allow_unverified_ssl {
log.Warn("ControllerConfigure: allow_unverified_ssl is set - will not check certificates!") log.Warn("ControllerConfigure: allow_unverified_ssl is set - will not check certificates!")
transCfg := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true},} transCfg := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
ret_config.cc_client = &http.Client{ ret_config.cc_client = &http.Client{
Transport: transCfg, Transport: transCfg,
Timeout: Timeout180s, Timeout: Timeout180s,
} }
} else { } else {
ret_config.cc_client = &http.Client{ ret_config.cc_client = &http.Client{
@@ -181,7 +180,7 @@ func ControllerConfigure(d *schema.ResourceData) (*ControllerCfg, error) {
tbuf.WriteString(claims["username"].(string)) tbuf.WriteString(claims["username"].(string))
tbuf.WriteString("@") tbuf.WriteString("@")
tbuf.WriteString(claims["iss"].(string)) tbuf.WriteString(claims["iss"].(string))
ret_config.decort_username = tbuf.String() ret_config.decort_username = tbuf.String()
} else { } else {
return nil, fmt.Errorf("Failed to extract user and iss fields from JWT token in oauth2 mode.") return nil, fmt.Errorf("Failed to extract user and iss fields from JWT token in oauth2 mode.")
} }
@@ -195,7 +194,7 @@ func ControllerConfigure(d *schema.ResourceData) (*ControllerCfg, error) {
return ret_config, nil return ret_config, nil
} }
func (config *ControllerCfg) getDecortUsername() (string) { func (config *ControllerCfg) getDecortUsername() string {
return config.decort_username return config.decort_username
} }
@@ -216,7 +215,7 @@ func (config *ControllerCfg) getOAuth2JWT() (string, error) {
params.Add("validity", "3600") params.Add("validity", "3600")
params_str := params.Encode() params_str := params.Encode()
req, err := http.NewRequest("POST", config.oauth2_url + "/v1/oauth/access_token", strings.NewReader(params_str)) req, err := http.NewRequest("POST", config.oauth2_url+"/v1/oauth/access_token", strings.NewReader(params_str))
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -232,14 +231,14 @@ func (config *ControllerCfg) getOAuth2JWT() (string, error) {
// fmt.Println("response Headers:", resp.Header) // fmt.Println("response Headers:", resp.Header)
// fmt.Println("response Headers:", req.URL) // fmt.Println("response Headers:", req.URL)
return "", fmt.Errorf("getOauth2JWT: unexpected status code %d when obtaining JWT from %q for APP_ID %q, request Body %q", return "", fmt.Errorf("getOauth2JWT: unexpected status code %d when obtaining JWT from %q for APP_ID %q, request Body %q",
resp.StatusCode, req.URL, config.app_id, params_str) resp.StatusCode, req.URL, config.app_id, params_str)
} }
defer resp.Body.Close() defer resp.Body.Close()
responseData, err := ioutil.ReadAll(resp.Body) responseData, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
return "", err return "", err
} }
// validation successful - store JWT in the corresponding field of the ControllerCfg structure // validation successful - store JWT in the corresponding field of the ControllerCfg structure
config.jwt = strings.TrimSpace(string(responseData)) config.jwt = strings.TrimSpace(string(responseData))
@@ -249,10 +248,10 @@ func (config *ControllerCfg) getOAuth2JWT() (string, error) {
func (config *ControllerCfg) validateJWT(jwt string) (bool, error) { func (config *ControllerCfg) validateJWT(jwt string) (bool, error) {
/* /*
Validate JWT against DECORT controller. JWT can be supplied as argument to this method. If empty string supplied as Validate JWT against DECORT controller. JWT can be supplied as argument to this method. If empty string supplied as
argument, JWT will be taken from config attribute. argument, JWT will be taken from config attribute.
DECORT controller URL will always be taken from the config attribute assigned at instantiation. DECORT controller URL will always be taken from the config attribute assigned at instantiation.
Validation is accomplished by attempting API call that lists accounts for the invoking user. Validation is accomplished by attempting API call that lists accounts for the invoking user.
*/ */
if jwt == "" { if jwt == "" {
if config.jwt == "" { if config.jwt == "" {
@@ -265,7 +264,7 @@ func (config *ControllerCfg) validateJWT(jwt string) (bool, error) {
return false, fmt.Errorf("validateJWT method called, but no OAuth2 URL provided.") return false, fmt.Errorf("validateJWT method called, but no OAuth2 URL provided.")
} }
req, err := http.NewRequest("POST", config.controller_url + "/restmachine/cloudapi/accounts/list", nil) req, err := http.NewRequest("POST", config.controller_url+"/restmachine/cloudapi/accounts/list", nil)
if err != nil { if err != nil {
return false, err return false, err
} }
@@ -280,7 +279,7 @@ func (config *ControllerCfg) validateJWT(jwt string) (bool, error) {
} }
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return false, fmt.Errorf("validateJWT: unexpected status code %d when validating JWT against %q.", return false, fmt.Errorf("validateJWT: unexpected status code %d when validating JWT against %q.",
resp.StatusCode, req.URL) resp.StatusCode, req.URL)
} }
defer resp.Body.Close() defer resp.Body.Close()
@@ -289,10 +288,10 @@ func (config *ControllerCfg) validateJWT(jwt string) (bool, error) {
func (config *ControllerCfg) validateLegacyUser() (bool, error) { func (config *ControllerCfg) validateLegacyUser() (bool, error) {
/* /*
Validate legacy user by obtaining a session key, which will be used for authenticating subsequent API calls Validate legacy user by obtaining a session key, which will be used for authenticating subsequent API calls
to DECORT controller. to DECORT controller.
If successful, the session key is stored in config.legacy_sid and true is returned. If unsuccessful for any If successful, the session key is stored in config.legacy_sid and true is returned. If unsuccessful for any
reason, the method will return false and error. reason, the method will return false and error.
*/ */
if config.auth_mode_code == MODE_UNDEF { if config.auth_mode_code == MODE_UNDEF {
return false, fmt.Errorf("validateLegacyUser method called for undefined authorization mode.") return false, fmt.Errorf("validateLegacyUser method called for undefined authorization mode.")
@@ -306,7 +305,7 @@ func (config *ControllerCfg) validateLegacyUser() (bool, error) {
params.Add("password", config.legacy_password) params.Add("password", config.legacy_password)
params_str := params.Encode() params_str := params.Encode()
req, err := http.NewRequest("POST", config.controller_url + "/restmachine/cloudapi/users/authenticate", strings.NewReader(params_str)) req, err := http.NewRequest("POST", config.controller_url+"/restmachine/cloudapi/users/authenticate", strings.NewReader(params_str))
if err != nil { if err != nil {
return false, err return false, err
} }
@@ -320,14 +319,14 @@ func (config *ControllerCfg) validateLegacyUser() (bool, error) {
} }
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return false, fmt.Errorf("validateLegacyUser: unexpected status code %d when validating legacy user %q against %q.", return false, fmt.Errorf("validateLegacyUser: unexpected status code %d when validating legacy user %q against %q.",
resp.StatusCode, config.legacy_user, config.controller_url) resp.StatusCode, config.legacy_user, config.controller_url)
} }
defer resp.Body.Close() defer resp.Body.Close()
responseData, err := ioutil.ReadAll(resp.Body) responseData, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
// validation successful - keep session ID for future use // validation successful - keep session ID for future use
config.legacy_sid = string(responseData) config.legacy_sid = string(responseData)
@@ -367,12 +366,13 @@ func (config *ControllerCfg) decortAPICall(method string, api_name string, url_v
} }
params_str := url_values.Encode() params_str := url_values.Encode()
req, err := http.NewRequest(method, config.controller_url + api_name, strings.NewReader(params_str)) req, err := http.NewRequest(method, config.controller_url+api_name, strings.NewReader(params_str))
if err != nil { if err != nil {
return "", err return "", err
} }
req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Content-Length", strconv.Itoa(len(params_str))) req.Header.Set("Content-Length", strconv.Itoa(len(params_str)))
req.Header.Set("Accept", "application/json")
if config.auth_mode_code == MODE_OAUTH2 || config.auth_mode_code == MODE_JWT { if config.auth_mode_code == MODE_OAUTH2 || config.auth_mode_code == MODE_JWT {
req.Header.Set("Authorization", fmt.Sprintf("bearer %s", config.jwt)) req.Header.Set("Authorization", fmt.Sprintf("bearer %s", config.jwt))
@@ -384,25 +384,23 @@ func (config *ControllerCfg) decortAPICall(method string, api_name string, url_v
} }
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode == http.StatusOK { if resp.StatusCode == http.StatusOK {
tmp_body, err := ioutil.ReadAll(resp.Body) body, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
return "", err return "", err
} }
json_resp := Jo2JSON(string(tmp_body)) log.Debugf("decortAPICall: %s %s\n %s", method, api_name, body)
log.Debugf("decortAPICall: %s %s\n %s", method, api_name, json_resp) return string(body), nil
return json_resp, nil
} else { } else {
return "", fmt.Errorf("decortAPICall: unexpected status code %d when calling API %q with request Body %q", return "", fmt.Errorf("decortAPICall: unexpected status code %d when calling API %q with request Body %q",
resp.StatusCode, req.URL, params_str) resp.StatusCode, req.URL, params_str)
} }
/* /*
if resp.StatusCode == StatusServiceUnavailable { if resp.StatusCode == StatusServiceUnavailable {
return nil, fmt.Errorf("decortAPICall method called for incompatible authorization mode %q.", config.auth_mode_txt) return nil, fmt.Errorf("decortAPICall method called for incompatible authorization mode %q.", config.auth_mode_txt)
} }
*/ */
return "", err return "", err
} }

View File

@@ -263,7 +263,7 @@ func flattenCompute(d *schema.ResourceData, compFacts string) error {
d.Set("rg_name", model.RgName) d.Set("rg_name", model.RgName)
d.Set("account_id", model.AccountID) d.Set("account_id", model.AccountID)
d.Set("account_name", model.AccountName) d.Set("account_name", model.AccountName)
d.Set("arch", model.Arch) d.Set("driver", model.Driver)
d.Set("cpu", model.Cpu) d.Set("cpu", model.Cpu)
d.Set("ram", model.Ram) d.Set("ram", model.Ram)
// d.Set("boot_disk_size", model.BootDiskSize) - bootdiskSize key in API compute/get is always zero, so we set boot_disk_size in another way // d.Set("boot_disk_size", model.BootDiskSize) - bootdiskSize key in API compute/get is always zero, so we set boot_disk_size in another way
@@ -367,7 +367,7 @@ func dataSourceCompute() *schema.Resource {
Description: "Name of the account this compute instance belongs to.", Description: "Name of the account this compute instance belongs to.",
}, },
"arch": { "driver": {
Type: schema.TypeString, Type: schema.TypeString,
Computed: true, Computed: true,
Description: "Hardware architecture of this compute instance.", Description: "Hardware architecture of this compute instance.",

View File

@@ -356,6 +356,7 @@ type ComputeGetResp struct {
Cpu int `json:"cpus"` Cpu int `json:"cpus"`
Desc string `json:"desc"` Desc string `json:"desc"`
Disks []DiskRecord `json:"disks"` Disks []DiskRecord `json:"disks"`
Driver string `json:"driver"`
GridID int `json:"gid"` GridID int `json:"gid"`
ID uint `json:"id"` ID uint `json:"id"`
ImageID int `json:"imageId"` ImageID int `json:"imageId"`

View File

@@ -27,9 +27,9 @@ import (
func parseOsUsers(logins []OsUserRecord) []interface{} { func parseOsUsers(logins []OsUserRecord) []interface{} {
var result = make([]interface{}, len(logins)) var result = make([]interface{}, len(logins))
elem := make(map[string]interface{})
for index, value := range logins { for index, value := range logins {
elem := make(map[string]interface{})
elem["guid"] = value.Guid elem["guid"] = value.Guid
elem["login"] = value.Login elem["login"] = value.Login
elem["password"] = value.Password elem["password"] = value.Password

View File

@@ -103,7 +103,7 @@ func Provider() *schema.Provider {
"decort_kvmvm": resourceCompute(), "decort_kvmvm": resourceCompute(),
"decort_disk": resourceDisk(), "decort_disk": resourceDisk(),
"decort_vins": resourceVins(), "decort_vins": resourceVins(),
// "decort_pfw": resourcePfw(), "decort_pfw": resourcePfw(),
}, },
DataSourcesMap: map[string]*schema.Resource{ DataSourcesMap: map[string]*schema.Resource{

202
decort/resource_pfw.go Normal file
View File

@@ -0,0 +1,202 @@
/*
Copyright (c) 2019-2021 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Author: Petr Krutov, <petr.krutov@digitalenergy.online>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration
Technology platfom.
Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates.
*/
package decort
import (
"fmt"
"net/url"
"strconv"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
log "github.com/sirupsen/logrus"
)
func resourcePfwCreate(d *schema.ResourceData, m interface{}) error {
log.Debugf("resourcePfwCreate: called for compute %d", d.Get("compute_id").(int))
controller := m.(*ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("computeId", strconv.Itoa(d.Get("compute_id").(int)))
urlValues.Add("publicPortStart", strconv.Itoa(d.Get("public_port_start").(int)))
urlValues.Add("localBasePort", strconv.Itoa(d.Get("local_base_port").(int)))
urlValues.Add("proto", d.Get("proto").(string))
if portEnd, ok := d.GetOk("public_port_end"); ok {
urlValues.Add("publicPortEnd", strconv.Itoa(portEnd.(int)))
}
pfwId, err := controller.decortAPICall("POST", ComputePfwAddAPI, urlValues)
if err != nil {
return err
}
d.SetId(fmt.Sprintf("%d-%s", d.Get("compute_id").(int), pfwId))
pfw, err := utilityPfwCheckPresence(d, m)
if err != nil {
return err
}
d.Set("local_ip", pfw.LocalIP)
if _, ok := d.GetOk("public_port_end"); !ok {
d.Set("public_port_end", pfw.PublicPortEnd)
}
return nil
}
func resourcePfwRead(d *schema.ResourceData, m interface{}) error {
log.Debugf("resourcePfwRead: called for compute %d, rule %s", d.Get("compute_id").(int), d.Id())
pfw, err := utilityPfwCheckPresence(d, m)
if pfw == nil {
if err != nil {
return err
}
return nil
}
d.Set("compute_id", pfw.ComputeID)
d.Set("public_port_start", pfw.PublicPortStart)
d.Set("public_port_end", pfw.PublicPortEnd)
d.Set("local_ip", pfw.LocalIP)
d.Set("local_base_port", pfw.LocalPort)
d.Set("proto", pfw.Protocol)
return nil
}
func resourcePfwDelete(d *schema.ResourceData, m interface{}) error {
log.Debugf("resourcePfwDelete: called for compute %d, rule %s", d.Get("compute_id").(int), d.Id())
pfw, err := utilityPfwCheckPresence(d, m)
if pfw == nil {
if err != nil {
return err
}
return nil
}
controller := m.(*ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("computeId", strconv.Itoa(d.Get("compute_id").(int)))
urlValues.Add("ruleId", d.Id())
_, err = controller.decortAPICall("POST", ComputePfwDelAPI, urlValues)
if err != nil {
return err
}
return nil
}
func resourcePfwExists(d *schema.ResourceData, m interface{}) (bool, error) {
log.Debugf("resourcePfwExists: called for compute %d, rule %s", d.Get("compute_id").(int), d.Id())
pfw, err := utilityPfwCheckPresence(d, m)
if pfw == nil {
if err != nil {
return false, err
}
return false, nil
}
return true, nil
}
func resourcePfwSchemaMake() map[string]*schema.Schema {
return map[string]*schema.Schema{
"compute_id": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
Description: "ID of compute instance.",
},
"public_port_start": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
ValidateFunc: validation.IntBetween(1, 65535),
Description: "External start port number for the rule.",
},
"public_port_end": {
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
ValidateFunc: validation.IntBetween(1, 65535),
Description: "End port number (inclusive) for the ranged rule.",
},
"local_ip": {
Type: schema.TypeString,
Computed: true,
Description: "IP address of compute instance.",
},
"local_base_port": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
ValidateFunc: validation.IntBetween(1, 65535),
Description: "Internal base port number.",
},
"proto": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringInSlice([]string{"tcp", "udp"}, false),
Description: "Network protocol, either 'tcp' or 'udp'.",
},
}
}
func resourcePfw() *schema.Resource {
return &schema.Resource{
SchemaVersion: 1,
Create: resourcePfwCreate,
Read: resourcePfwRead,
Delete: resourcePfwDelete,
Exists: resourcePfwExists,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Timeouts: &schema.ResourceTimeout{
Create: &Timeout60s,
Read: &Timeout30s,
Update: &Timeout60s,
Delete: &Timeout60s,
Default: &Timeout60s,
},
Schema: resourcePfwSchemaMake(),
}
}

44
decort/utility_pfw.go Normal file
View File

@@ -0,0 +1,44 @@
package decort
import (
"encoding/json"
"net/url"
"strconv"
"strings"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)
func utilityPfwCheckPresence(d *schema.ResourceData, m interface{}) (*PfwRecord, error) {
controller := m.(*ControllerCfg)
urlValues := &url.Values{}
urlValues.Add("computeId", strconv.Itoa(d.Get("compute_id").(int)))
resp, err := controller.decortAPICall("POST", ComputePfwListAPI, urlValues)
if err != nil {
return nil, err
}
if resp == "" {
return nil, nil
}
idS := strings.Split(d.Id(), "-")[1]
id, err := strconv.Atoi(idS)
if err != nil {
return nil, err
}
var pfws []PfwRecord
if err := json.Unmarshal([]byte(resp), &pfws); err != nil {
return nil, err
}
for _, pfw := range pfws {
if pfw.ID == id {
return &pfw, nil
}
}
return nil, nil
}

View File

@@ -29,12 +29,12 @@ description: |-
- **account_id** (Number) ID of the account this compute instance belongs to. - **account_id** (Number) ID of the account this compute instance belongs to.
- **account_name** (String) Name of the account this compute instance belongs to. - **account_name** (String) Name of the account this compute instance belongs to.
- **arch** (String) Hardware architecture of this compute instance.
- **boot_disk_id** (Number) This compute instance boot disk ID. - **boot_disk_id** (Number) This compute instance boot disk ID.
- **boot_disk_size** (Number) This compute instance boot disk size in GB. - **boot_disk_size** (Number) This compute instance boot disk size in GB.
- **cloud_init** (String) Placeholder for cloud_init parameters. - **cloud_init** (String) Placeholder for cloud_init parameters.
- **cpu** (Number) Number of CPUs allocated for this compute instance. - **cpu** (Number) Number of CPUs allocated for this compute instance.
- **description** (String) User-defined text description of this compute instance. - **description** (String) User-defined text description of this compute instance.
- **driver** (String) Hardware architecture of this compute instance.
- **extra_disks** (Set of Number) IDs of the extra disk(s) attached to this compute. - **extra_disks** (Set of Number) IDs of the extra disk(s) attached to this compute.
- **image_id** (Number) ID of the OS image this compute instance is based on. - **image_id** (Number) ID of the OS image this compute instance is based on.
- **image_name** (String) Name of the OS image this compute instance is based on. - **image_name** (String) Name of the OS image this compute instance is based on.

46
docs/resources/pfw.md Normal file
View File

@@ -0,0 +1,46 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "decort_pfw Resource - terraform-provider-decort"
subcategory: ""
description: |-
---
# decort_pfw (Resource)
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- **compute_id** (Number) ID of compute instance.
- **local_base_port** (Number) Internal base port number.
- **proto** (String) Network protocol, either 'tcp' or 'udp'.
- **public_port_start** (Number) External start port number for the rule.
### Optional
- **id** (String) The ID of this resource.
- **public_port_end** (Number) End port number (inclusive) for the ranged rule.
- **timeouts** (Block, Optional) (see [below for nested schema](#nestedblock--timeouts))
### Read-Only
- **local_ip** (String) IP address of compute instance.
<a id="nestedblock--timeouts"></a>
### Nested Schema for `timeouts`
Optional:
- **create** (String)
- **default** (String)
- **delete** (String)
- **read** (String)
- **update** (String)