diff --git a/main.go b/main.go
index b790e855..1c0dd2a4 100644
--- a/main.go
+++ b/main.go
@@ -785,6 +785,142 @@ func main() {
 		}
 		log.Printf("Network configuration on node %s has been reverted\n", node)
 
+	//SDN
+	case "applySDN":
+		exitStatus, err := c.ApplySDN()
+		if err != nil {
+			failError(fmt.Errorf("error: %+v\n api error: %s", err, exitStatus))
+		}
+		log.Printf("SDN configuration has been applied\n")
+
+	case "getZonesList":
+		zones, err := c.GetSDNZones(true, "")
+		if err != nil {
+			log.Printf("Error listing SDN zones %+v\n", err)
+			os.Exit(1)
+		}
+		zonesList, err := json.Marshal(zones)
+		failError(err)
+		fmt.Println(string(zonesList))
+
+	case "getZone":
+		if len(flag.Args()) < 2 {
+			failError(fmt.Errorf("error: Zone name is needed"))
+		}
+		zoneName := flag.Args()[1]
+		zone, err := c.GetSDNZone(zoneName)
+		if err != nil {
+			log.Printf("Error listing SDN zones %+v\n", err)
+			os.Exit(1)
+		}
+		zoneList, err := json.Marshal(zone)
+		failError(err)
+		fmt.Println(string(zoneList))
+
+	case "createZone":
+		if len(flag.Args()) < 2 {
+			failError(fmt.Errorf("error: Zone name is needed"))
+		}
+		zoneName := flag.Args()[1]
+		config, err := proxmox.NewConfigSDNZoneFromJson(GetConfig(*fConfigFile))
+		failError(err)
+		failError(config.CreateWithValidate(zoneName, c))
+		log.Printf("Zone %s has been created\n", zoneName)
+
+	case "deleteZone":
+		if len(flag.Args()) < 2 {
+			failError(fmt.Errorf("error: zone name required"))
+		}
+		zoneName := flag.Args()[1]
+		err := c.DeleteSDNZone(zoneName)
+		failError(err)
+
+	case "updateZone":
+		if len(flag.Args()) < 2 {
+			failError(fmt.Errorf("error: zone name required"))
+		}
+		zoneName := flag.Args()[1]
+		config, err := proxmox.NewConfigSDNZoneFromJson(GetConfig(*fConfigFile))
+		failError(err)
+		failError(config.UpdateWithValidate(zoneName, c))
+		log.Printf("Zone %s has been updated\n", zoneName)
+
+	case "getVNetsList":
+		zones, err := c.GetSDNVNets(true)
+		if err != nil {
+			log.Printf("Error listing SDN zones %+v\n", err)
+			os.Exit(1)
+		}
+		vnetsList, err := json.Marshal(zones)
+		failError(err)
+		fmt.Println(string(vnetsList))
+
+	case "getVNet":
+		if len(flag.Args()) < 2 {
+			failError(fmt.Errorf("error: VNet name is needed"))
+		}
+		vnetName := flag.Args()[1]
+		vnet, err := c.GetSDNVNet(vnetName)
+		if err != nil {
+			log.Printf("Error listing SDN VNets %+v\n", err)
+			os.Exit(1)
+		}
+		vnetsList, err := json.Marshal(vnet)
+		failError(err)
+		fmt.Println(string(vnetsList))
+
+	case "createVNet":
+		if len(flag.Args()) < 2 {
+			failError(fmt.Errorf("error: VNet name is needed"))
+		}
+		vnetName := flag.Args()[1]
+		config, err := proxmox.NewConfigSDNVNetFromJson(GetConfig(*fConfigFile))
+		failError(err)
+		failError(config.CreateWithValidate(vnetName, c))
+		log.Printf("VNet %s has been created\n", vnetName)
+
+	case "deleteVNet":
+		if len(flag.Args()) < 2 {
+			failError(fmt.Errorf("error: VNet name required"))
+		}
+		vnetName := flag.Args()[1]
+		err := c.DeleteSDNVNet(vnetName)
+		failError(err)
+
+	case "updateVNet":
+		if len(flag.Args()) < 2 {
+			failError(fmt.Errorf("error: zone name required"))
+		}
+		vnetName := flag.Args()[1]
+		config, err := proxmox.NewConfigSDNVNetFromJson(GetConfig(*fConfigFile))
+		failError(err)
+		failError(config.UpdateWithValidate(vnetName, c))
+		log.Printf("VNet %s has been updated\n", vnetName)
+
+	case "getDNSList":
+		dns, err := c.GetSDNDNSs("")
+		if err != nil {
+			log.Printf("Error listing SDN DNS entries %+v\n", err)
+			os.Exit(1)
+		}
+		dnsList, err := json.Marshal(dns)
+		failError(err)
+		fmt.Println(string(dnsList))
+
+	case "getDNS":
+		if len(flag.Args()) < 2 {
+			failError(fmt.Errorf("error: DNS name is needed"))
+		}
+		name := flag.Args()[1]
+		dns, err := c.GetSDNDNS(name)
+		if err != nil {
+			log.Printf("Error listing SDN DNS %+v\n", err)
+			os.Exit(1)
+		}
+		dnsList, err := json.Marshal(dns)
+		failError(err)
+		fmt.Println(string(dnsList))
+
 	default:
 		fmt.Printf("unknown action, try start|stop vmid\n")
 	}
diff --git a/proxmox/client.go b/proxmox/client.go
index b6b365d0..548c2c8f 100644
--- a/proxmox/client.go
+++ b/proxmox/client.go
@@ -1833,6 +1833,173 @@ func (c *Client) RevertNetwork(node string) (exitStatus string, err error) {
 	return c.DeleteWithTask(url)
 }
 
+// SDN
+
+func (c *Client) ApplySDN() (string, error) {
+	return c.PutWithTask(nil, "/cluster/sdn")
+}
+
+// GetSDNVNets returns a list of all VNet definitions in the "data" element of the returned
+// map.
+func (c *Client) GetSDNVNets(pending bool) (list map[string]interface{}, err error) {
+	url := fmt.Sprintf("/cluster/sdn/vnets?pending=%d", Btoi(pending))
+	err = c.GetJsonRetryable(url, &list, 3)
+	return
+}
+
+// CheckSDNVNetExistance returns true if a DNS entry with the provided ID exists, false otherwise.
+func (c *Client) CheckSDNVNetExistance(id string) (existance bool, err error) {
+	list, err := c.GetSDNVNets(true)
+	existance = ItemInKeyOfArray(list["data"].([]interface{}), "vnet", id)
+	return
+}
+
+// GetSDNVNet returns details about the DNS entry whose name was provided.
+// An error is returned if the zone doesn't exist.
+// The returned zone can be unmarshalled into a ConfigSDNVNet struct.
+func (c *Client) GetSDNVNet(name string) (dns map[string]interface{}, err error) {
+	url := fmt.Sprintf("/cluster/sdn/vnets/%s", name)
+	err = c.GetJsonRetryable(url, &dns, 3)
+	return
+}
+
+// CreateSDNVNet creates a new SDN DNS in the cluster
+func (c *Client) CreateSDNVNet(params map[string]interface{}) error {
+	return c.Post(params, "/cluster/sdn/vnets")
+}
+
+// DeleteSDNVNet deletes an existing SDN DNS in the cluster
+func (c *Client) DeleteSDNVNet(name string) error {
+	return c.Delete(fmt.Sprintf("/cluster/sdn/vnets/%s", name))
+}
+
+// UpdateSDNVNet updates the given DNS with the provided parameters
+func (c *Client) UpdateSDNVNet(id string, params map[string]interface{}) error {
+	return c.Put(params, "/cluster/sdn/vnets/"+id)
+}
+
+// GetSDNSubnets returns a list of all Subnet definitions in the "data" element of the returned
+// map.
+func (c *Client) GetSDNSubnets(vnet string) (list map[string]interface{}, err error) {
+	url := fmt.Sprintf("/cluster/sdn/vnets/%s/subnets", vnet)
+	err = c.GetJsonRetryable(url, &list, 3)
+	return
+}
+
+// CheckSDNSubnetExistance returns true if a DNS entry with the provided ID exists, false otherwise.
+func (c *Client) CheckSDNSubnetExistance(vnet, id string) (existance bool, err error) {
+	list, err := c.GetSDNSubnets(vnet)
+	existance = ItemInKeyOfArray(list["data"].([]interface{}), "subnet", id)
+	return
+}
+
+// GetSDNSubnet returns details about the Subnet entry whose name was provided.
+// An error is returned if the zone doesn't exist.
+// The returned map["data"] section can be unmarshalled into a ConfigSDNSubnet struct.
+func (c *Client) GetSDNSubnet(vnet, name string) (subnet map[string]interface{}, err error) {
+	url := fmt.Sprintf("/cluster/sdn/vnets/%s/subnets/%s", vnet, name)
+	err = c.GetJsonRetryable(url, &subnet, 3)
+	return
+}
+
+// CreateSDNSubnet creates a new SDN DNS in the cluster
+func (c *Client) CreateSDNSubnet(vnet string, params map[string]interface{}) error {
+	return c.Post(params, fmt.Sprintf("/cluster/sdn/vnets/%s/subnets", vnet))
+}
+
+// DeleteSDNSubnet deletes an existing SDN DNS in the cluster
+func (c *Client) DeleteSDNSubnet(vnet, name string) error {
+	return c.Delete(fmt.Sprintf("/cluster/sdn/vnets/%s/subnets/%s", vnet, name))
+}
+
+// UpdateSDNSubnet updates the given DNS with the provided parameters
+func (c *Client) UpdateSDNSubnet(vnet, id string, params map[string]interface{}) error {
+	return c.Put(params, fmt.Sprintf("/cluster/sdn/vnets/%s/subnets/%s", vnet, id))
+}
+
+// GetSDNDNSs returns a list of all DNS definitions in the "data" element of the returned
+// map.
+func (c *Client) GetSDNDNSs(typeFilter string) (list map[string]interface{}, err error) {
+	url := "/cluster/sdn/dns"
+	if typeFilter != "" {
+		url += fmt.Sprintf("&type=%s", typeFilter)
+	}
+	err = c.GetJsonRetryable(url, &list, 3)
+	return
+}
+
+// CheckSDNDNSExistance returns true if a DNS entry with the provided ID exists, false otherwise.
+func (c *Client) CheckSDNDNSExistance(id string) (existance bool, err error) {
+	list, err := c.GetSDNDNSs("")
+	existance = ItemInKeyOfArray(list["data"].([]interface{}), "dns", id)
+	return
+}
+
+// GetSDNDNS returns details about the DNS entry whose name was provided.
+// An error is returned if the zone doesn't exist.
+// The returned zone can be unmarshalled into a ConfigSDNDNS struct.
+func (c *Client) GetSDNDNS(name string) (dns map[string]interface{}, err error) {
+	url := fmt.Sprintf("/cluster/sdn/dns/%s", name)
+	err = c.GetJsonRetryable(url, &dns, 3)
+	return
+}
+
+// CreateSDNDNS creates a new SDN DNS in the cluster
+func (c *Client) CreateSDNDNS(params map[string]interface{}) error {
+	return c.Post(params, "/cluster/sdn/dns")
+}
+
+// DeleteSDNDNS deletes an existing SDN DNS in the cluster
+func (c *Client) DeleteSDNDNS(name string) error {
+	return c.Delete(fmt.Sprintf("/cluster/sdn/dns/%s", name))
+}
+
+// UpdateSDNDNS updates the given DNS with the provided parameters
+func (c *Client) UpdateSDNDNS(id string, params map[string]interface{}) error {
+	return c.Put(params, "/cluster/sdn/dns/"+id)
+}
+
+// GetSDNZones returns a list of all the SDN zones defined in the cluster.
+func (c *Client) GetSDNZones(pending bool, typeFilter string) (list map[string]interface{}, err error) {
+	url := fmt.Sprintf("/cluster/sdn/zones?pending=%d", Btoi(pending))
+	if typeFilter != "" {
+		url += fmt.Sprintf("&type=%s", typeFilter)
+	}
+	err = c.GetJsonRetryable(url, &list, 3)
+	return
+}
+
+// CheckSDNZoneExistance returns true if a zone with the provided ID exists, false otherwise.
+func (c *Client) CheckSDNZoneExistance(id string) (existance bool, err error) {
+	list, err := c.GetSDNZones(true, "")
+	existance = ItemInKeyOfArray(list["data"].([]interface{}), "zone", id)
+	return
+}
+
+// GetSDNZone returns details about the zone whose name was provided.
+// An error is returned if the zone doesn't exist.
+// The returned zone can be unmarshalled into a ConfigSDNZone struct.
+func (c *Client) GetSDNZone(zoneName string) (zone map[string]interface{}, err error) {
+	url := fmt.Sprintf("/cluster/sdn/zones/%s", zoneName)
+	err = c.GetJsonRetryable(url, &zone, 3)
+	return
+}
+
+// CreateSDNZone creates a new SDN zone in the cluster
+func (c *Client) CreateSDNZone(params map[string]interface{}) error {
+	return c.Post(params, "/cluster/sdn/zones")
+}
+
+// DeleteSDNZone deletes an existing SDN zone in the cluster
+func (c *Client) DeleteSDNZone(zoneName string) error {
+	return c.Delete(fmt.Sprintf("/cluster/sdn/zones/%s", zoneName))
+}
+
+// UpdateSDNZone updates the given zone with the provided parameters
+func (c *Client) UpdateSDNZone(id string, params map[string]interface{}) error {
+	return c.Put(params, "/cluster/sdn/zones/"+id)
+}
+
 // Shared
 func (c *Client) GetItemConfigMapStringInterface(url, text, message string) (map[string]interface{}, error) {
 	data, err := c.GetItemConfig(url, text, message)
diff --git a/proxmox/config_sdn_dns.go b/proxmox/config_sdn_dns.go
new file mode 100644
index 00000000..de97d188
--- /dev/null
+++ b/proxmox/config_sdn_dns.go
@@ -0,0 +1,91 @@
+package proxmox
+
+import (
+	"encoding/json"
+	"fmt"
+)
+
+// ConfigSDNDNS describes the SDN DNS configurable element
+type ConfigSDNDNS struct {
+	DNS  string `json:"dns"`
+	Key  string `json:"key"`
+	Type string `json:"type"`
+	URL  string `json:"url"`
+	TTL  int    `json:"ttl,omitempty"`
+	// The SDN Plugin schema contains ReverseV6Mask attribute while the
+	// PowerDNS plugin schema contains the ReverseMaskV6 attribute
+	// This is probably a bug that crept into the Proxmox implementation.a
+	// Checked in libpve-network-perl=0.7.3
+	ReverseMaskV6 int `json:"reversemaskv6,omitempty"`
+	ReverseV6Mask int `json:"reversev6mask,omitempty"`
+	// Digest allows for a form of optimistic locking
+	Digest string `json:"digest,omitempty"`
+}
+
+func NewConfigSDNDNSFromJson(input []byte) (config *ConfigSDNDNS, err error) {
+	config = &ConfigSDNDNS{}
+	err = json.Unmarshal([]byte(input), config)
+	return
+}
+
+func (config *ConfigSDNDNS) CreateWithValidate(id string, client *Client) (err error) {
+	err = config.Validate(id, true, client)
+	if err != nil {
+		return
+	}
+	return config.Create(id, client)
+}
+
+func (config *ConfigSDNDNS) Create(id string, client *Client) (err error) {
+	config.DNS = id
+	params := config.mapToApiValues()
+	return client.CreateSDNDNS(params)
+}
+
+func (config *ConfigSDNDNS) UpdateWithValidate(id string, client *Client) (err error) {
+	err = config.Validate(id, false, client)
+	if err != nil {
+		return
+	}
+	return config.Update(id, client)
+}
+
+func (config *ConfigSDNDNS) Update(id string, client *Client) (err error) {
+	config.DNS = id
+	params := config.mapToApiValues()
+	err = client.UpdateSDNDNS(id, params)
+	if err != nil {
+		params, _ := json.Marshal(&params)
+		return fmt.Errorf("error updating SDN DNS: %v, (params: %v)", err, string(params))
+	}
+	return
+}
+
+func (c *ConfigSDNDNS) Validate(id string, create bool, client *Client) (err error) {
+	exists, err := client.CheckSDNDNSExistance(id)
+	if err != nil {
+		return
+	}
+	if exists && create {
+		return ErrorItemExists(id, "dns")
+	}
+	if !exists && !create {
+		return ErrorItemNotExists(id, "dns")
+	}
+
+	err = ValidateStringInArray([]string{"powerdns"}, c.Type, "type")
+	if err != nil {
+		return
+	}
+	err = ValidateIntGreater(0, c.TTL, "ttl")
+	if err != nil {
+		return
+	}
+	return
+}
+
+func (config *ConfigSDNDNS) mapToApiValues() (params map[string]interface{}) {
+	d, _ := json.Marshal(config)
+	json.Unmarshal(d, &params)
+	return
+}
diff --git a/proxmox/config_sdn_subnet.go b/proxmox/config_sdn_subnet.go
new file mode 100644
index 00000000..0f9a2a81
--- /dev/null
+++ b/proxmox/config_sdn_subnet.go
@@ -0,0 +1,123 @@
+package proxmox
+
+import (
+	"encoding/json"
+	"fmt"
+	"net"
+)
+
+type ConfigSDNSubnet struct {
+	// For creation purposes - Subnet is a CIDR
+	// Once a subnet has been created, the Subnet is an identifier with the format
+	// "<zone>-<ip>-<mask>"
+	Subnet string `json:"subnet"`
+
+	DNSZonePrefix string `json:"dnszoneprefix,omitempty"`
+	Gateway       string `json:"gateway,omitempty"`
+	SNAT          bool   `json:"snat,omitempty"`
+
+	// Delete is a string of attributes to be deleted from the object
+	Delete string `json:"delete,omitempty"`
+	// Type must always hold the string "subnet"
+	Type string `json:"type"`
+	// Digest allows for a form of optimistic locking
+	Digest string `json:"digest,omitempty"`
+}
+
+// NewConfigSDNSubnetFromJSON takes in a byte array from a json encoded SDN Subnet
+// configuration and stores it in config.
+// It returns the newly created config with the passed in configuration stored
+// and an error if one occurs unmarshalling the input data.
+func NewConfigSDNSubnetFromJson(input []byte) (config *ConfigSDNSubnet, err error) {
+	config = &ConfigSDNSubnet{}
+	err = json.Unmarshal([]byte(input), config)
+	return
+}
+
+func (config *ConfigSDNSubnet) CreateWithValidate(vnet, id string, client *Client) (err error) {
+	err = config.Validate(vnet, id, true, client)
+	if err != nil {
+		return
+	}
+	return config.Create(vnet, id, client)
+}
+
+func (config *ConfigSDNSubnet) Create(vnet, id string, client *Client) (err error) {
+	config.Subnet = id
+	config.Type = "subnet"
+	params := config.mapToApiValues()
+	return client.CreateSDNSubnet(vnet, params)
+}
+
+func (config *ConfigSDNSubnet) UpdateWithValidate(vnet, id string, client *Client) (err error) {
+	err = config.Validate(vnet, id, false, client)
+	if err != nil {
+		return
+	}
+	return config.Update(vnet, id, client)
+}
+
+func (config *ConfigSDNSubnet) Update(vnet, id string, client *Client) (err error) {
+	config.Subnet = id
+	config.Type = "" // For some reason, this shouldn't be sent on update. Only on create.
+	params := config.mapToApiValues()
+	err = client.UpdateSDNSubnet(vnet, id, params)
+	if err != nil {
+		params, _ := json.Marshal(&params)
+		return fmt.Errorf("error updating SDN Subnet: %v, (params: %v)", err, string(params))
+	}
+	return
+}
+
+func (c *ConfigSDNSubnet) Validate(vnet, id string, create bool, client *Client) (err error) {
+	vnetExists, err := client.CheckSDNVNetExistance(vnet)
+	if err != nil {
+		return
+	}
+	if !vnetExists {
+		return fmt.Errorf("subnet must be created in an existing vnet. vnet (%s) wasn't found", vnet)
+	}
+	exists, err := client.CheckSDNSubnetExistance(vnet, id)
+	if err != nil {
+		return
+	}
+	if exists && create {
+		return ErrorItemExists(id, "subnet")
+	}
+	if !exists && !create {
+		return ErrorItemNotExists(id, "subnet")
+	}
+
+	// if this is an update, the Subnet is an identifier of the form <zone>-<ip>-<mask>
+	// and therefore shouldn't be validated or changed
+	if create {
+		// Make sure that the CIDR is actually a valid CIDR
+		_, _, err = net.ParseCIDR(c.Subnet)
+		if err != nil {
+			return
+		}
+	}
+
+	if c.Gateway != "" {
+		ip := net.ParseIP(c.Gateway)
+		if ip == nil {
+			return fmt.Errorf("error gateway (%s) is not a valid IP", c.Gateway)
+		}
+	}
+
+	return
+}
+
+func (config *ConfigSDNSubnet) mapToApiValues() (params map[string]interface{}) {
+
+	d, _ := json.Marshal(config)
+	json.Unmarshal(d, &params)
+
+	if v, has := params["snat"]; has {
+		params["snat"] = Btoi(v.(bool))
+	}
+	// Remove the subnet and vnet (path parameters) from the map
+	delete(params, "subnet")
+	delete(params, "vnet")
+	return
+}
diff --git a/proxmox/config_sdn_vnet.go b/proxmox/config_sdn_vnet.go
new file mode 100644
index 00000000..f7793226
--- /dev/null
+++ b/proxmox/config_sdn_vnet.go
@@ -0,0 +1,100 @@
+package proxmox
+
+import (
+	"encoding/json"
+	"fmt"
+	"regexp"
+)
+
+type ConfigSDNVNet struct {
+	VNet      string `json:"vnet"`
+	Zone      string `json:"zone"`
+	Alias     string `json:"alias,omitempty"`
+	Delete    string `json:"delete,omitempty"`
+	Tag       int    `json:"tag,omitempty"`
+	VLANAware bool   `json:"vlanaware,omitempty"`
+	// Digest allows for a form of optimistic locking
+	Digest string `json:"digest,omitempty"`
+}
+
+func NewConfigSDNVNetFromJson(input []byte) (config *ConfigSDNVNet, err error) {
+	config = &ConfigSDNVNet{}
+	err = json.Unmarshal([]byte(input), config)
+	return
+}
+
+func (config *ConfigSDNVNet) CreateWithValidate(id string, client *Client) (err error) {
+	err = config.Validate(id, true, client)
+	if err != nil {
+		return
+	}
+	return config.Create(id, client)
+}
+
+func (config *ConfigSDNVNet) Create(id string, client *Client) (err error) {
+	config.VNet = id
+	params := config.mapToApiValues()
+	return client.CreateSDNVNet(params)
+}
+
+func (config *ConfigSDNVNet) UpdateWithValidate(id string, client *Client) (err error) {
+	err = config.Validate(id, false, client)
+	if err != nil {
+		return
+	}
+	return config.Update(id, client)
+}
+
+func (config *ConfigSDNVNet) Update(id string, client *Client) (err error) {
+	config.VNet = id
+	params := config.mapToApiValues()
+	err = client.UpdateSDNVNet(id, params)
+	if err != nil {
+		params, _ := json.Marshal(&params)
+		return fmt.Errorf("error updating SDN VNet: %v, (params: %v)", err, string(params))
+	}
+	return
+}
+
+func (c *ConfigSDNVNet) Validate(id string, create bool, client *Client) (err error) {
+	exists, err := client.CheckSDNVNetExistance(id)
+	if err != nil {
+		return
+	}
+	if exists && create {
+		return ErrorItemExists(id, "vnet")
+	}
+	if !exists && !create {
+		return ErrorItemNotExists(id, "vnet")
+	}
+	zoneExists, err := client.CheckSDNZoneExistance(c.Zone)
+	if err != nil {
+		return
+	}
+	if !zoneExists {
+		return fmt.Errorf("vnet must be associated to an existing zone. zone %s could not be found", c.Zone)
+	}
+	if c.Alias != "" {
+		regex, _ := regexp.Compile(`^(?i:[\(\)-_.\w\d\s]{0,256})$`)
+		if !regex.Match([]byte(c.Alias)) {
+			return fmt.Errorf(`alias must match the validation regular expression: ^(?i:[\(\)-_.\w\d\s]{0,256})$`)
+		}
+	}
+	err = ValidateIntGreater(0, c.Tag, "tag")
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+func (config *ConfigSDNVNet) mapToApiValues() (params map[string]interface{}) {
+	d, _ := json.Marshal(config)
+	json.Unmarshal(d, &params)
+
+	if v, has := params["vlanaware"]; has {
+		params["vlanaware"] = Btoi(v.(bool))
+	}
+
+	return
+}
diff --git a/proxmox/config_sdn_zone.go b/proxmox/config_sdn_zone.go
new file mode 100644
index 00000000..6dd9dc3e
--- /dev/null
+++ b/proxmox/config_sdn_zone.go
@@ -0,0 +1,163 @@
+package proxmox
+
+import (
+	"encoding/json"
+	"fmt"
+)
+
+// ConfigSDNZone describes the Zone configurable element
+type ConfigSDNZone struct {
+	Type                     string `json:"type"`
+	Zone                     string `json:"zone"`
+	AdvertiseSubnets         bool   `json:"advertise-subnets,omitempty"`
+	Bridge                   string `json:"bridge,omitempty"`
+	BridgeDisableMacLearning bool   `json:"bridge-disable-mac-learning,omitempty"`
+	Controller               string `json:"controller,omitempty"`
+	DisableARPNDSuppression  bool   `json:"disable-arp-nd-suppression,omitempty"`
+	DNS                      string `json:"dns,omitempty"`
+	DNSZone                  string `json:"dnszone,omitempty"`
+	DPID                     int    `json:"dp-id,omitempty"`
+	ExitNodes                string `json:"exitnodes,omitempty"`
+	ExitNodesLocalRouting    bool   `json:"exitnodes-local-routing,omitempty"`
+	ExitNodesPrimary         string `json:"exitnodes-primary,omitempty"`
+	IPAM                     string `json:"ipam,omitempty"`
+	MAC                      string `json:"mac,omitempty"`
+	MTU                      int    `json:"mtu,omitempty"`
+	Nodes                    string `json:"nodes,omitempty"`
+	Peers                    string `json:"peers,omitempty"`
+	ReverseDNS               string `json:"reversedns,omitempty"`
+	RTImport                 string `json:"rt-import,omitempty"`
+	Tag                      int    `json:"tag,omitempty"`
+	VlanProtocol             string `json:"vlan-protocol,omitempty"`
+	VrfVxlan                 int    `json:"vrf-vxlan,omitempty"`
+	// Pass a string of attributes to be deleted from the remote object
+	Delete string `json:"delete,omitempty"`
+	// Digest allows for a form of optimistic locking
+	Digest string `json:"digest,omitempty"`
+}
+
+// NewConfigNetworkFromJSON takes in a byte array from a json encoded SDN Zone
+// configuration and stores it in config.
+// It returns the newly created config with the passed in configuration stored
+// and an error if one occurs unmarshalling the input data.
+func NewConfigSDNZoneFromJson(input []byte) (config *ConfigSDNZone, err error) {
+	config = &ConfigSDNZone{}
+	err = json.Unmarshal([]byte(input), config)
+	return
+}
+
+func (config *ConfigSDNZone) CreateWithValidate(id string, client *Client) (err error) {
+	err = config.Validate(id, true, client)
+	if err != nil {
+		return
+	}
+	return config.Create(id, client)
+}
+
+func (config *ConfigSDNZone) Create(id string, client *Client) (err error) {
+	config.Zone = id
+	params := config.mapToApiValues()
+	return client.CreateSDNZone(params)
+}
+
+func (config *ConfigSDNZone) UpdateWithValidate(id string, client *Client) (err error) {
+	err = config.Validate(id, false, client)
+	if err != nil {
+		return
+	}
+	return config.Update(id, client)
+}
+
+func (config *ConfigSDNZone) Update(id string, client *Client) (err error) {
+	config.Zone = id
+	params := config.mapToApiValues()
+	err = client.UpdateSDNZone(id, params)
+	if err != nil {
+		params, _ := json.Marshal(&params)
+		return fmt.Errorf("error updating SDN Zone: %v, (params: %v)", err, string(params))
+	}
+	return
+}
+
+func (c *ConfigSDNZone) Validate(id string, create bool, client *Client) (err error) {
+	exists, err := client.CheckSDNZoneExistance(id)
+	if err != nil {
+		return
+	}
+	if exists && create {
+		return ErrorItemExists(id, "zone")
+	}
+	if !exists && !create {
+		return ErrorItemNotExists(id, "zone")
+	}
+
+	err = ValidateStringInArray([]string{"evpn", "qinq", "simple", "vlan", "vxlan"}, c.Type, "type")
+	if err != nil {
+		return
+	}
+	switch c.Type {
+	case "simple":
+	case "vlan":
+		if create {
+			if c.Bridge == "" {
+				return ErrorKeyEmpty("bridge")
+			}
+		}
+	case "qinq":
+		if create {
+			if c.Bridge == "" {
+				return ErrorKeyEmpty("bridge")
+			}
+			if c.Tag <= 0 {
+				return ErrorKeyEmpty("tag")
+			}
+			if c.VlanProtocol == "" {
+				return ErrorKeyEmpty("vlan-protocol")
+			}
+		}
+	case "vxlan":
+		if create {
+			if c.Peers == "" {
+				return ErrorKeyEmpty("peers")
+			}
+		}
+	case "evpn":
+		if create {
+			if c.VrfVxlan < 0 {
+				return ErrorKeyEmpty("vrf-vxlan")
+			}
+			if c.Controller == "" {
+				return ErrorKeyEmpty("controller")
+			}
+		}
+	}
+	if c.VlanProtocol != "" {
+		err = ValidateStringInArray([]string{"802.1q", "802.1ad"}, c.VlanProtocol, "vlan-protocol")
+		if err != nil {
+			return
+		}
+	}
+	return
+}
+
+func (config *ConfigSDNZone) mapToApiValues() (params map[string]interface{}) {
+
+	d, _ := json.Marshal(config)
+	json.Unmarshal(d, &params)
+
+	boolsToFix := []string{
+		"advertise-subnets",
+		"bridge-disable-mac-learning",
+		"disable-arp-nd-suppression",
+		"exitnodes-local-routing",
+	}
+	for _, key := range boolsToFix {
+		if v, has := params[key]; has {
+			params[key] = Btoi(v.(bool))
+		}
+	}
+	// Remove the zone and type (path parameters) from the map
+	delete(params, "zone")
+	delete(params, "type")
+	return
+}
diff --git a/proxmox/util.go b/proxmox/util.go
index 400f91e6..34eec5fe 100644
--- a/proxmox/util.go
+++ b/proxmox/util.go
@@ -19,6 +19,15 @@ func inArray(arr []string, str string) bool {
 	return false
 }
 
+func Btoi(b bool) int {
+	switch b {
+	case true:
+		return 1
+	default:
+		return 0
+	}
+}
+
 func Itob(i int) bool {
 	return i == 1
 }