Skip to content

Commit

Permalink
add support dynamic network zone
Browse files Browse the repository at this point in the history
Signed-off-by: Tien Nguyen <duytien.nguyen@okta.com>
  • Loading branch information
duytiennguyen-okta committed Aug 6, 2024
1 parent 5bde109 commit b021e65
Show file tree
Hide file tree
Showing 7 changed files with 451 additions and 93 deletions.
9 changes: 9 additions & 0 deletions examples/resources/okta_network_zone/basic.tf
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,12 @@ resource "okta_network_zone" "dynamic_proxy_example" {
usage = "BLOCKLIST"
dynamic_proxy_type = "TorAnonymizer"
}

resource "okta_network_zone" "dynamic_v2_network_zone_example" {
name = "testAcc_replace_with_uuid Dynamic V2"
type = "DYNAMIC_V2"
status = "ACTIVE"
dynamic_locations = ["US", "AF-BGL"]
ip_service_categories_include = ["ALL_IP_SERVICES"]
ip_service_categories_exclude = ["SURFSHARK_VPN"]
}
8 changes: 8 additions & 0 deletions examples/resources/okta_network_zone/basic_updated.tf
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,11 @@ resource "okta_network_zone" "dynamic_proxy_example" {
usage = "POLICY"
dynamic_proxy_type = "NotTorAnonymizer"
}

resource "okta_network_zone" "dynamic_v2_network_zone_example" {
name = "testAcc_replace_with_uuid Dynamic V2 Updated"
type = "DYNAMIC_V2"
status = "ACTIVE"
dynamic_locations_exclude = ["BE-VAN", "CN-BJ"]
ip_service_categories_include = ["SYMANTEC_VPN", "TRENDMICRO_VPN", "GOOGLE_VPN"]
}
125 changes: 125 additions & 0 deletions okta/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/logging"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/okta/okta-sdk-golang/v4/okta"
v5okta "github.com/okta/okta-sdk-golang/v5/okta"

Check failure on line 22 in okta/config.go

View workflow job for this annotation

GitHub Actions / build

no required module provides package github.com/okta/okta-sdk-golang/v5/okta; to add it:
"github.com/okta/terraform-provider-okta/okta/internal/apimutex"
"github.com/okta/terraform-provider-okta/okta/internal/transport"
"github.com/okta/terraform-provider-okta/sdk"
Expand Down Expand Up @@ -59,6 +60,7 @@ type (
maxAPICapacity int // experimental
oktaSDKClientV2 *sdk.Client
oktaSDKClientV3 *okta.APIClient
oktaSDKClientV5 *v5okta.APIClient
oktaSDKsupplementClient *sdk.APISupplement
logger hclog.Logger
queriedWellKnown bool
Expand Down Expand Up @@ -249,6 +251,12 @@ func (c *Config) loadClients(ctx context.Context) error {
}
c.oktaSDKClientV3 = v3Client

v5Client, err := oktaV5SDKClient(c)
if err != nil {
return err
}
c.oktaSDKClientV5 = v5Client

// TODO: remove sdk client when v3 client is fully utilized within the provider
client, err := oktaSDKClient(c)
if err != nil {
Expand Down Expand Up @@ -532,6 +540,123 @@ func oktaV3SDKClient(c *Config) (client *okta.APIClient, err error) {
return client, nil
}

func oktaV5SDKClient(c *Config) (client *v5okta.APIClient, err error) {
var httpClient *http.Client
logLevel := strings.ToLower(os.Getenv("TF_LOG"))
debugHttpRequests := (logLevel == "1" || logLevel == "debug" || logLevel == "trace")
if c.backoff {
retryableClient := retryablehttp.NewClient()
retryableClient.RetryWaitMin = time.Second * time.Duration(c.minWait)
retryableClient.RetryWaitMax = time.Second * time.Duration(c.maxWait)
retryableClient.RetryMax = c.retryCount
retryableClient.Logger = c.logger
if debugHttpRequests {
// Needed for pretty printing http protocol in a local developer environment, ignore deprecation warnings.
//lint:ignore SA1019 used in developer mode only
retryableClient.HTTPClient.Transport = logging.NewTransport("Okta", retryableClient.HTTPClient.Transport)
} else {
retryableClient.HTTPClient.Transport = logging.NewSubsystemLoggingHTTPTransport("Okta", retryableClient.HTTPClient.Transport)
}
retryableClient.ErrorHandler = errHandler
retryableClient.CheckRetry = checkRetry
httpClient = retryableClient.StandardClient()
c.logger.Info(fmt.Sprintf("running with backoff http client, wait min %d, wait max %d, retry max %d", retryableClient.RetryWaitMin, retryableClient.RetryWaitMax, retryableClient.RetryMax))
} else {
httpClient = cleanhttp.DefaultClient()
if debugHttpRequests {
// Needed for pretty printing http protocol in a local developer environment, ignore deprecation warnings.
//lint:ignore SA1019 used in developer mode only
httpClient.Transport = logging.NewTransport("Okta", httpClient.Transport)
} else {
httpClient.Transport = logging.NewSubsystemLoggingHTTPTransport("Okta", httpClient.Transport)
}
c.logger.Info("running with default http client")
}

// adds transport governor to retryable or default client
if c.maxAPICapacity > 0 && c.maxAPICapacity < 100 {
c.logger.Info(fmt.Sprintf("running with experimental max_api_capacity configuration at %d%%", c.maxAPICapacity))
apiMutex, err := apimutex.NewAPIMutex(c.maxAPICapacity)
if err != nil {
return nil, err
}
httpClient.Transport = transport.NewGovernedTransport(httpClient.Transport, apiMutex, c.logger)
}
var orgUrl string
var disableHTTPS bool
if c.httpProxy != "" {
orgUrl = strings.TrimSuffix(c.httpProxy, "/")
disableHTTPS = strings.HasPrefix(orgUrl, "http://")
} else {
orgUrl = fmt.Sprintf("https://%v.%v", c.orgName, c.domain)
}
_, err = url.Parse(orgUrl)
if err != nil {
return nil, fmt.Errorf("malformed Okta API URL (org_name+base_url value, or http_proxy value): %+v", err)
}

setters := []v5okta.ConfigSetter{
v5okta.WithOrgUrl(orgUrl),
v5okta.WithCache(false),
v5okta.WithHttpClientPtr(httpClient),
v5okta.WithRateLimitMaxBackOff(int64(c.maxWait)),
v5okta.WithRequestTimeout(int64(c.requestTimeout)),
v5okta.WithRateLimitMaxRetries(int32(c.retryCount)),
v5okta.WithUserAgentExtra(OktaTerraformProviderUserAgent),
}
// v3 client also needs http proxy explicitly set
if c.httpProxy != "" {
_url, err := url.Parse(c.httpProxy)
if err != nil {
return nil, err
}
host := v5okta.WithProxyHost(_url.Hostname())
setters = append(setters, host)

sPort := _url.Port()
if sPort == "" {
sPort = "80"
}
iPort, err := strconv.Atoi(sPort)
if err != nil {
return nil, err
}
port := v5okta.WithProxyPort(int32(iPort))
setters = append(setters, port)
}

switch {
case c.accessToken != "":
setters = append(
setters,
v5okta.WithToken(c.accessToken), v5okta.WithAuthorizationMode("Bearer"),
)

case c.apiToken != "":
setters = append(
setters,
v5okta.WithToken(c.apiToken), v5okta.WithAuthorizationMode("SSWS"),
)

case c.privateKey != "":
setters = append(
setters,
v5okta.WithPrivateKey(c.privateKey), v5okta.WithPrivateKeyId(c.privateKeyId), v5okta.WithScopes(c.scopes), v5okta.WithClientId(c.clientID), v5okta.WithAuthorizationMode("PrivateKey"),
)
}

if disableHTTPS {
setters = append(setters, v5okta.WithTestingDisableHttpsCheck(true))
}

config, err := v5okta.NewConfiguration(setters...)
if err != nil {
return nil, err
}
client = v5okta.NewAPIClient(config)
return client, nil
}

func errHandler(resp *http.Response, err error, numTries int) (*http.Response, error) {
if err != nil {
return resp, err
Expand Down
87 changes: 57 additions & 30 deletions okta/data_source_okta_network_zone.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/okta/terraform-provider-okta/sdk"
v5okta "github.com/okta/okta-sdk-golang/v5/okta"
)

func dataSourceNetworkZone() *schema.Resource {
Expand All @@ -28,30 +28,36 @@ func dataSourceNetworkZone() *schema.Resource {
"dynamic_locations": {
Type: schema.TypeSet,
Computed: true,
Description: "Array of locations ISO-3166-1(2). Format code: countryCode OR countryCode-regionCode",
Description: "Array of locations ISO-3166-1(2) included. Format code: countryCode OR countryCode-regionCode. Use with type `DYNAMIC` or `DYNAMIC_V2`",
Elem: &schema.Schema{Type: schema.TypeString},
},
"dynamic_locations_exclude": {
Type: schema.TypeSet,
Optional: true,
Description: "Array of locations ISO-3166-1(2) excluded. Format code: countryCode OR countryCode-regionCode. Use with type `DYNAMIC_V2`",
Elem: &schema.Schema{Type: schema.TypeString},
},
"dynamic_proxy_type": {
Type: schema.TypeString,
Computed: true,
Description: "Type of proxy being controlled by this network zone",
Description: "Type of proxy being controlled by this dynamic network zone - can be one of `Any`, `TorAnonymizer` or `NotTorAnonymizer`. Use with type `DYNAMIC`",
},
"gateways": {
Type: schema.TypeSet,
Computed: true,
Description: "Array of values in CIDR/range form depending on the way it's been declared (i.e. CIDR will contain /suffix). Please check API docs for examples",
Description: "Array of values in CIDR/range form depending on the way it's been declared (i.e. CIDR will contain /suffix). Please check API docs for examples. Use with type `IP`",
Elem: &schema.Schema{Type: schema.TypeString},
},
"proxies": {
Type: schema.TypeSet,
Computed: true,
Description: "Array of values in CIDR/range form depending on the way it's been declared (i.e. CIDR will contain /suffix). Please check API docs for examples",
Description: "Array of values in CIDR/range form depending on the way it's been declared (i.e. CIDR will contain /suffix). Please check API docs for examples. Can not be set if `usage` is set to `BLOCKLIST`. Use with type `IP`",
Elem: &schema.Schema{Type: schema.TypeString},
},
"type": {
Type: schema.TypeString,
Computed: true,
Description: "Type of the Network Zone - can either be IP or DYNAMIC only",
Description: "Type of the Network Zone - can be `IP`, `DYNAMIC` or `DYNAMIC_V2` only",
},
"usage": {
Type: schema.TypeString,
Expand All @@ -61,14 +67,26 @@ func dataSourceNetworkZone() *schema.Resource {
"asns": {
Type: schema.TypeSet,
Computed: true,
Description: "Format of each array value: a string representation of an ASN numeric value",
Description: "List of asns included. Format of each array value: a string representation of an ASN numeric value. Use with type `DYNAMIC` or `DYNAMIC_V2`",
Elem: &schema.Schema{Type: schema.TypeString},
},
"status": {
Type: schema.TypeString,
Computed: true,
Description: "Network Status - can either be ACTIVE or INACTIVE only",
},
"ip_service_categories_include": {
Type: schema.TypeSet,
Optional: true,
Description: "List of ip service included. Use with type `DYNAMIC_V2`",
Elem: &schema.Schema{Type: schema.TypeString},
},
"ip_service_categories_exclude": {
Type: schema.TypeSet,
Optional: true,
Description: "List of ip service excluded. Use with type `DYNAMIC_V2`",
Elem: &schema.Schema{Type: schema.TypeString},
},
},
Description: "Gets Okta Network Zone.",
}
Expand All @@ -82,55 +100,50 @@ func dataSourceNetworkZoneRead(ctx context.Context, d *schema.ResourceData, m in
}
var (
err error
zone *sdk.NetworkZone
zone *v5okta.ListNetworkZones200ResponseInner
)
if id != "" {
zone, _, err = getOktaClientFromMetadata(m).NetworkZone.GetNetworkZone(ctx, id)
zone, _, err = getOktaV5ClientFromMetadata(m).NetworkZoneAPI.GetNetworkZone(ctx, id).Execute()
} else {
zone, err = findNetworkZoneByName(ctx, m, name)
}
if err != nil {
return diag.Errorf("failed to find network zone: %v", err)
}
d.SetId(zone.Id)
_ = d.Set("name", zone.Name)
_ = d.Set("type", zone.Type)
_ = d.Set("status", zone.Status)
_ = d.Set("usage", zone.Usage)
_ = d.Set("dynamic_proxy_type", zone.ProxyType)
_ = d.Set("asns", convertStringSliceToSetNullable(zone.Asns))
err = setNonPrimitives(d, map[string]interface{}{
"gateways": flattenAddresses(zone.Gateways),
"proxies": flattenAddresses(zone.Proxies),
"dynamic_locations": flattenDynamicLocations(zone.Locations),
})
nzID, err := concreteNetworkzoneID(zone)
if err != nil {
return diag.Errorf("failed to create network zone: %v", err)
}
d.SetId(nzID)

err = mapNetworkZoneToState(d, zone)
if err != nil {
return diag.Errorf("failed to set network zone properties: %v", err)
}
return nil
}

func findNetworkZoneByName(ctx context.Context, m interface{}, name string) (*sdk.NetworkZone, error) {
client := getOktaClientFromMetadata(m)
zones, resp, err := client.NetworkZone.ListNetworkZones(ctx, nil)
func findNetworkZoneByName(ctx context.Context, m interface{}, name string) (*v5okta.ListNetworkZones200ResponseInner, error) {
client := getOktaV5ClientFromMetadata(m)
zones, resp, err := client.NetworkZoneAPI.ListNetworkZones(ctx).Execute()
if err != nil {
return nil, err
}
for i := range zones {
if zones[i].Name == name {
return zones[i], nil
if getNetworkZoneName(zones[i]) == name {
return &zones[i], nil
}
}
for {
var moreZones []*sdk.NetworkZone
var moreZones []v5okta.ListNetworkZones200ResponseInner
if resp.HasNextPage() {
resp, err = resp.Next(ctx, &moreZones)
resp, err = resp.Next(&moreZones)
if err != nil {
return nil, err
}
for i := range moreZones {
if moreZones[i].Name == name {
return moreZones[i], nil
if getNetworkZoneName(moreZones[i]) == name {
return &zones[i], nil
}
}
} else {
Expand All @@ -139,3 +152,17 @@ func findNetworkZoneByName(ctx context.Context, m interface{}, name string) (*sd
}
return nil, fmt.Errorf("network zone with name '%s' does not exist", name)
}

func getNetworkZoneName(data v5okta.ListNetworkZones200ResponseInner) string {
var name string
nz := data.GetActualInstance()
switch v := nz.(type) {
case *v5okta.DynamicNetworkZone:
name = v.GetName()
case *v5okta.EnhancedDynamicNetworkZone:
name = v.GetName()
case *v5okta.IPNetworkZone:
name = v.GetName()
}
return name
}
Loading

0 comments on commit b021e65

Please sign in to comment.