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

Added support for ZoneDelegated object #379

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
84 changes: 1 addition & 83 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -1,88 +1,5 @@
# Infoblox IPAM Driver for Terraform

## Prerequisites

Whether you intend to use the published plug-in or the customized version that you have built yourself, you must complete the following prerequisites:

- Install and set up a physical or virtual Infoblox NIOS appliance and has necessary licenses installed. Configure the access permissions for Terraform to interact with NIOS Grid objects.
- To use the Infoblox IPAM Plug-In for Terraform, you must either define the following extensible attributes or install the Cloud Network Automation license in the NIOS Grid, which adds the extensible attributes by default:
```json
{
"Tenant ID": "String Type",
"CMP Type": "String Type",
"Cloud API Owned": "List Type (Values True, False)"
}
```
You may add other extensible attributes that you want to use.
- Create an extensible attribute by name Terraform Internal ID of type string in Infoblox NIOS as given in below curl command.
```bash
curl -k -u <user>:<password> -H "Content-Type: application/json" -X POST https://<Grid_IP>/wapi/v2.12/extensibleattributedef -d '{"name": "Terraform Internal ID", "flags": "CR", "type": "STRING", "comment": "Internal ID for Terraform Resource"}'
```

> **Note:**
>
>Either the Terraform Internal ID extensible attribute definition must be present in NIOS or IPAM Plug-In for Terraform
must be configured with superuser access for it to automatically create the extensible attribute. If not, the connection
to Terraform will fail.
>
>If you choose to create the Terraform Internal ID extensible attribute manually or by using the cURL command,
the creation of the extensible attribute is not managed by IPAM Plug-In for Terraform.
>
>You must not modify the Terraform Internal ID for a resource under any circumstances. If it is modified, the resource
will no longer be managed by Terraform.


## Configuring Infoblox Terraform IPAM Plug-In

Terraform relies on an Infoblox provider to interact with NIOS Grid objects. You can either use the published Infoblox provider (Infoblox IPAM Plug-In for Terraform) available on the Terraform Registry page or develop a plug-in with features that are not available in the published plug-in.

As a prerequisite, configure provider authentication to set up the required access permissions for Terraform to interact with NIOS Grid objects. Additionally, declare the version of IPAM Plug-In for Terraform in the .tf file to allow Terraform to automatically install the published plug-in available in the Terraform Registry.

To configure IPAM Plug-In for Terraform for use, complete the following steps:

In the .tf file, specify the plug-in version in the required_providers block as follows in .tf file:
```hcl
terraform {
required_providers {
infoblox = {
source = "infobloxopen/infoblox"
version = ">= 2.7.0"
}
}
}
```

Configure the credentials required to access the NIOS Grid as environment variables or provider block in .tf file:


```bash
# Using environment variable
$ export INFOBLOX_SERVER=<nios_ip-addr or nios_hostname>
$ export INFOBLOX_USERNAME=<nios_username>
$ export INFOBLOX_PASSWORD=<nios_password>
```

```hcl
// Using Provider block
provider "infoblox" {
server = var.server
username = var.username
password = var.password
}
```

Add other environment variables that you intend to use.
You can set the following environment variables instead of defining them as attributes inside the provider block in the .tf file. Each of these environment variables has a corresponding attribute in the provider block.
```
PORT
SSLMODE
CONNECT_TIMEOUT
POOL_CONNECTIONS
WAPI_VERSION
```

Run the terraform init command in the directory where the .tf file is located to initialize the plug-in.

## Resources

There are resources for the following objects, supported by the plugin:
Expand All @@ -101,6 +18,7 @@ There are resources for the following objects, supported by the plugin:
* Zone Auth (`infoblox_zone_auth`)
* Zone Forward (`infoblox_zone_forward`)
* Host record (`infoblox_ip_allocation` / `infoblox_ip_association`)
* Zone delegated

Network and network container resources have two versions: IPv4 and IPv6. In
addition, there are two operations which are implemented as resources:
Expand Down
40 changes: 40 additions & 0 deletions docs/resources/infoblox_zone_delegated.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Resource Zone Delegated

A Zone Delegated resource creates NS records for a subdomain, pointing to one or more external authoritative name servers. The `infoblox_zone_delegated` resource allow managing such delegations. The parent zone must already exist

The following list describes the parameters you can define in the `infoblox_zone_delegated` resource block:

## Argument Reference
* `fqdn`: (Required) The subdomain name to be delegated
* `delegate_to`: (Required) Nested block(s)s for the delegated name servers
* `name`: (Required) The FQDN of the name server
* `ext_attrs`: (Optional) A set of NIOS extensible attributes that are attached to the record, using jsonencode. Currently only "Tenant ID" is supported

## Attribute Reference
* `delegate_to`:
* `address`: The computed IP address for each delegated name server

## Example Usage

```hcl
resource "infoblox_zone_delegated" "subdomain" {

fqdn = "subdomain.test.com"

delegate_to {
name = "ns-1488.awsdns-58.org"
}

delegate_to {
name = "ns-2034.awsdns-62.co.uk"
}

}
```

## Import
Zone Delegated resources can be imported by using either the object reference or the subdomain fqdn, for example:
```shell script
# terraform import infoblox_zone_delegated.subdomain zone_delegated/ZG5zLnpvbmUkLl9kZWZhdWx0LmNvbS5jb2xsZWdlY2hvaWNldHJhbnNpdGlvbi5nc2xi:subdomain.test.com/default
# terraform import infoblox_zone_delegated.subdomain subdomain.test.com
```
15 changes: 15 additions & 0 deletions examples/v0.14/Resources/ZoneDelegated/infoblox.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Zone Delegated

resource "infoblox_zone_delegated" "subdomain" {

fqdn = "subdomain.example.com"

delegate_to {
name = "ns-1488.awsdns-58.org"
}

delegate_to {
name = "ns-2034.awsdns-62.co.uk"
}

}
8 changes: 8 additions & 0 deletions examples/v0.14/Resources/ZoneDelegated/versions.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
terraform {
required_providers {
infoblox = {
source = "infobloxopen/infoblox"
version = ">= 2.1"
}
}
}
201 changes: 201 additions & 0 deletions infoblox/datasource_infoblox_zone_delegated.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
package infoblox

import (
"context"
"encoding/json"
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
ibclient "github.com/infobloxopen/infoblox-go-client/v2"
"strconv"
"time"
)

func dataSourceZoneDelegated() *schema.Resource {
return &schema.Resource{
ReadContext: dataSourceZoneDelegatedRead,
Schema: map[string]*schema.Schema{
"filters": {
Type: schema.TypeMap,
Required: true,
},
"results": {
Type: schema.TypeList,
Computed: true,
Description: "List of Forward Zones matching filters",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"id": {
Type: schema.TypeString,
Computed: true,
},
"fqdn": {
Type: schema.TypeString,
Required: true,
Description: "The FQDN of the delegated zone.",
},
"delegate_to": {
Type: schema.TypeSet,
Optional: true,
Description: "The Infoblox appliance redirects queries for data for the delegated zone to this remote name server.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"address": {
Type: schema.TypeString,
Required: true,
Description: "The IPv4 Address or IPv6 Address of the server.",
},
"name": {
Type: schema.TypeString,
Required: true,
Description: "A resolvable domain name for the external DNS server.",
},
},
},
},
"comment": {
Type: schema.TypeString,
Optional: true,
Description: "A descriptive comment.",
},
"disable": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "Determines if the zone is disabled or not.",
},
"locked": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "If you enable this flag, other administrators cannot make conflicting changes. This is for administration purposes only. " +
"The zone will continue to serve DNS data even when it is locked.",
},
"ns_group": {
Type: schema.TypeString,
Optional: true,
Description: "The delegation NS group bound with delegated zone.",
},
"delegated_ttl": {
Type: schema.TypeInt,
Optional: true,
Default: ttlUndef,
Description: "TTL value for zone-delegated.",
},
"ext_attrs": {
Type: schema.TypeString,
Default: "",
Optional: true,
Description: "Extensible attributes, as a map in JSON format",
},
"view": {
Type: schema.TypeString,
Optional: true,
Default: "default",
Description: "The DNS view in which the zone is created.",
},
"zone_format": {
Type: schema.TypeString,
Optional: true,
Default: "FORWARD",
Description: "The format of the zone. Valid values are: FORWARD, IPV4, IPV6.",
},
},
},
},
},
}
}

func dataSourceZoneDelegatedRead(_ context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
connector := m.(ibclient.IBConnector)

var diags diag.Diagnostics

filters := filterFromMap(d.Get("filters").(map[string]interface{}))

objMgr := ibclient.NewObjectManager(connector, "Terraform", "")

qp := ibclient.NewQueryParams(false, filters)
res, err := objMgr.GetZoneDelegatedByFilters(qp)

Check failure on line 120 in infoblox/datasource_infoblox_zone_delegated.go

View workflow job for this annotation

GitHub Actions / Test (ubuntu-latest, 1.21)

objMgr.GetZoneDelegatedByFilters undefined (type ibclient.IBObjectManager has no field or method GetZoneDelegatedByFilters)

Check failure on line 120 in infoblox/datasource_infoblox_zone_delegated.go

View workflow job for this annotation

GitHub Actions / Test (macOS-latest, 1.21)

objMgr.GetZoneDelegatedByFilters undefined (type ibclient.IBObjectManager has no field or method GetZoneDelegatedByFilters)
if err != nil {
return diag.FromErr(fmt.Errorf("failed to get zone delegated records: %w", err))
}

if res == nil {
return diag.FromErr(fmt.Errorf("API returns a nil/empty ID for zone delegated"))
}
// TODO: temporary scaffold, need to rework marshalling/unmarshalling of EAs
// (avoiding additional layer of keys ("value" key)
results := make([]interface{}, 0, len(res))
for _, r := range res {
zoneDelegatedFlat, err := flattenZoneDelegated(r)
if err != nil {
return diag.FromErr(fmt.Errorf("failed to flatten zone delegated : %w", err))
}
results = append(results, zoneDelegatedFlat)
}

err = d.Set("results", results)
if err != nil {
return diag.FromErr(err)
}

// always run
d.SetId(strconv.FormatInt(time.Now().Unix(), 10))

return diags

}

func flattenZoneDelegated(zoneDelegated ibclient.ZoneDelegated) (map[string]interface{}, error) {
var eaMap map[string]interface{}
if zoneDelegated.Ea != nil && len(zoneDelegated.Ea) > 0 {
eaMap = zoneDelegated.Ea
} else {
eaMap = make(map[string]interface{})
}

ea, err := json.Marshal(eaMap)
if err != nil {
return nil, err
}

res := map[string]interface{}{
"id": zoneDelegated.Ref,
"fqdn": zoneDelegated.Fqdn,
"ext_attrs": string(ea),
"zone_format": zoneDelegated.ZoneFormat,
"view": *zoneDelegated.View,
}
if zoneDelegated.Comment != nil {
res["comment"] = *zoneDelegated.Comment
}
if zoneDelegated.Disable != nil {
res["disable"] = *zoneDelegated.Disable
}
if zoneDelegated.Locked != nil {
res["locked"] = *zoneDelegated.Locked
}
if zoneDelegated.NsGroup != nil {
res["ns_group"] = *zoneDelegated.NsGroup
}
if zoneDelegated.UseDelegatedTtl != nil {
if !*zoneDelegated.UseDelegatedTtl {
res["delegated_ttl"] = ttlUndef
}
}

if zoneDelegated.DelegatedTtl != nil && *zoneDelegated.DelegatedTtl > 0 {
res["delegated_ttl"] = *zoneDelegated.DelegatedTtl
} else {
res["delegated_ttl"] = ttlUndef
}

if zoneDelegated.DelegateTo.IsNull == false {

Check failure on line 195 in infoblox/datasource_infoblox_zone_delegated.go

View workflow job for this annotation

GitHub Actions / Test (ubuntu-latest, 1.21)

zoneDelegated.DelegateTo.IsNull undefined (type []ibclient.NameServer has no field or method IsNull)

Check failure on line 195 in infoblox/datasource_infoblox_zone_delegated.go

View workflow job for this annotation

GitHub Actions / Test (macOS-latest, 1.21)

zoneDelegated.DelegateTo.IsNull undefined (type []ibclient.NameServer has no field or method IsNull)
nsInterface := convertNullableNameServersToInterface(zoneDelegated.DelegateTo)
res["delegate_to"] = nsInterface
}

return res, nil
}
Loading
Loading