Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,35 @@ The following attributes are exported:
listing.
* `dns_resolver_id` - The ID of the DNS resolver to use in the section.

#### The `phpipam_first_free_subnet` Data Source
The `phpipam_first_free_subnet` data source returns first available subnet within given `master_subnet_id` for specified `subnet_mask`

**Example:**
```
data "phpipam_first_free_subnet" "subnet" {
master_subnet_id = "9"
subnet_mask = "26"
}

output subnet_cidr_block {
value = "${data.phpipam_first_free_subnet.subnet.subnet_address}"
}
```

##### Argument Reference

The data source takes the following parameters:
* `master_subnet_id` (Required) - The ID of the parent subnet for this subnet
* `subnet_mask` (Required) - The subnet mask of first available subnet, in bits.

##### Attribute Reference

The following attributes are exported:

* `subnet_address` - The network address of the subnet
subnet.


#### The `phpipam_subnet` Data Source

The `phpipam_subnet` data source gets information on a subnet such as its ID
Expand All @@ -425,6 +454,7 @@ resource "phpipam_address" {
}
```


**Example with `description_match`:**

```
Expand Down Expand Up @@ -823,6 +853,44 @@ The following attributes are exported:
* `section_id` - The ID of the section in the PHPIPAM database.
* `edit_date` - The date this resource was last edited.

#### The `phpipam_first_free_subnet` Resource
The `phpipam_first_free_subnet` resouce can be used to checkout and manage a subnet in PHPIPAM. It is same as creating subnet using `phpipam_subnet` resource, with an option to checkout first free subnet from given `master_subnet_id` and `subnet_mask`

Example:
```
resource "phpipam_first_free_subnet" "subnet" {
master_subnet_id = 9
subnet_mask = 24
custom_fields = {
CustomTestSubnets = "terraform-test"
}

}

```
##### Argument Reference

The resouce takes following parameters:
* `master_subnet_id` (Required) - The ID of the parent subnet for this subnet
* `subnet_mask` (Required) - The subnet mask, in bits.

⚠️ **NOTE on custom fields:** PHPIPAM installations with custom fields must have
all fields set to optional when using this plugin. For more info see
[here](https://github.com/phpipam/phpipam/issues/1073). Further to this, either
ensure that your fields also do not have default values, or ensure the default
is set in your TF configuration. Diff loops may happen otherwise!

##### Attribute Reference

The following attributes are exported:

* `subnet_id` - The ID of the subnet in the PHPIPAM database.
* `subnet_address` - The network address of the subnet
* `permissions` - A JSON representation of the permissions associated with this
subnet.
* `edit_date` - The date this resource was last updated.


#### The `phpipam_subnet` Resource

The `phpipam_subnet` resource can be used to create and manage a subnet in
Expand Down
43 changes: 43 additions & 0 deletions plugin/providers/phpipam/data_source_phpipam_first_free_subnet.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package phpipam

import (
"fmt"
"errors"
"github.com/hashicorp/terraform/helper/schema"
)

func dataSourcePHPIPAMFirstFreeSubnet() *schema.Resource {
return &schema.Resource{
Read: dataSourcePHPIPAMFirstFreeSubnetRead,
Schema: map[string]*schema.Schema{
"master_subnet_id": &schema.Schema{
Type: schema.TypeInt,
Required: true,
},
"subnet_mask": &schema.Schema{
Type: schema.TypeInt,
Required: true,
},
"subnet_address": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
},
}
}

func dataSourcePHPIPAMFirstFreeSubnetRead(d *schema.ResourceData, meta interface{}) error {
c := meta.(*ProviderPHPIPAMClient).subnetsController
out, err := c.GetFirstFreeSubnet(d.Get("master_subnet_id").(int),d.Get("subnet_mask").(int))
if err != nil {
return err
}
if out == "" {
return errors.New(fmt.Sprintf("Master Subnet has no free subnet of size %s", d.Get("subnet_mask")))
}

d.SetId(out)
d.Set("subnet_address", out)

return nil
}
2 changes: 2 additions & 0 deletions plugin/providers/phpipam/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,14 @@ func Provider() terraform.ResourceProvider {
"phpipam_section": resourcePHPIPAMSection(),
"phpipam_subnet": resourcePHPIPAMSubnet(),
"phpipam_vlan": resourcePHPIPAMVLAN(),
"phpipam_first_free_subnet": resourcePHPIPAMFirstFreeSubnet(),
},

DataSourcesMap: map[string]*schema.Resource{
"phpipam_address": dataSourcePHPIPAMAddress(),
"phpipam_addresses": dataSourcePHPIPAMAddresses(),
"phpipam_first_free_address": dataSourcePHPIPAMFirstFreeAddress(),
"phpipam_first_free_subnet": dataSourcePHPIPAMFirstFreeSubnet(),
"phpipam_section": dataSourcePHPIPAMSection(),
"phpipam_subnet": dataSourcePHPIPAMSubnet(),
"phpipam_subnets": dataSourcePHPIPAMSubnets(),
Expand Down
58 changes: 58 additions & 0 deletions plugin/providers/phpipam/resource_phpipam_first_free_subnet.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package phpipam

import (
"fmt"
"strings"
"strconv"
"errors"
"github.com/hashicorp/terraform/helper/schema"
)

// resourcePHPIPAMSubnet returns the resource structure for the phpipam_subnet
// resource.
//
// Note that we use the data source read function here to pull down data, as
// read workflow is identical for both the resource and the data source.
func resourcePHPIPAMFirstFreeSubnet() *schema.Resource {
return &schema.Resource{
Create: resourcePHPIPAMFirstFreeSubnetCreate,
Read: dataSourcePHPIPAMSubnetRead,
Update: resourcePHPIPAMSubnetUpdate,
Delete: resourcePHPIPAMSubnetDelete,
Schema: resourceFirstFreeSubnetSchema(),
}
}

func resourcePHPIPAMFirstFreeSubnetCreate(d *schema.ResourceData, meta interface{}) error {
c := meta.(*ProviderPHPIPAMClient).subnetsController

id := d.Get("master_subnet_id").(int)
mask := d.Get("subnet_mask").(int)

message, err := c.CreateFirstFreeSubnet(id,mask);
if err != nil {
return err
}
cidr_mask := strings.Split(message, "/");
d.Set("subnet_address", cidr_mask[0])
if customFields, ok := d.GetOk("custom_fields"); ok {
subnets, err := c.GetSubnetsByCIDR(fmt.Sprintf("%s/%s", cidr_mask[0], cidr_mask[1]))

if err != nil {
return fmt.Errorf("Could not read subnet after creating: %s", err)
}

if len(subnets) != 1 {
return errors.New("Subnet either missing or multiple results returned by reading subnet after creation")
}

d.SetId(strconv.Itoa(subnets[0].ID))
d.Set("subnet_id", subnets[0].ID)
d.Set("subnet_address", subnets[0].SubnetAddress)
d.Set("subnet_mask", subnets[0].Mask)
if _, err := c.UpdateSubnetCustomFields(subnets[0].ID, customFields.(map[string]interface{})); err != nil {
return err
}
}
return dataSourcePHPIPAMSubnetRead(d, meta)
}
25 changes: 25 additions & 0 deletions plugin/providers/phpipam/subnet_structure.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,31 @@ func bareSubnetSchema() map[string]*schema.Schema {
}
}

// resourceSubnetSchema returns the schema for the phpipam_first_free_subnet resource. It
// sets the required and optional fields, the latter defined in
// resourceSubnetRequiredFields, and ensures that all optional and
// non-configurable fields are computed as well.
func resourceFirstFreeSubnetSchema() map[string]*schema.Schema {
schema := bareSubnetSchema()
for k, v := range schema {
switch {
// Subnet Address and Mask are currently ForceNew
case k == "master_subnet_id" || k == "subnet_mask" :
v.Required = true
v.ForceNew = true
case k == "custom_fields":
v.Optional = true
case resourceSubnetOptionalFields.Has(k):
v.Optional = true
v.Computed = true
default:
v.Computed = true
}
}
return schema
}


// resourceSubnetSchema returns the schema for the phpipam_subnet resource. It
// sets the required and optional fields, the latter defined in
// resourceSubnetRequiredFields, and ensures that all optional and
Expand Down

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.