diff --git a/netbox/client.go b/netbox/client.go
index 4269b8d65c39958439d473ccc8240f3c8e60b1c7..11dcf8a347ec434d4b2da5ad42b88165d862dec9 100644
--- a/netbox/client.go
+++ b/netbox/client.go
@@ -75,7 +75,7 @@ func NewClient(addr string, token string, client *http.Client) (*Client, error)
 	}
 
 	c.DCIM = NewDCIMService(c)
-	c.IPAM = &IPAMService{c: c}
+	c.IPAM = NewIPAMService(c)
 	c.Tenancy = NewTenancyService(c)
 
 	return c, nil
diff --git a/netbox/client_test.go b/netbox/client_test.go
index 01c150276213f35ccc7025aa9a3d278daccc78f7..c97d88a6188ff05e8db97e964dd7ca46e1c8808f 100644
--- a/netbox/client_test.go
+++ b/netbox/client_test.go
@@ -306,3 +306,43 @@ func testTenantWithGroup(n int, t *TenantGroup) *Tenant {
 		Group:       t,
 	}
 }
+
+func testTenantIdentifier(n int) *Tenant {
+	return &Tenant{
+		ID:   n,
+		Name: fmt.Sprintf("Tenant %d", n),
+		Slug: fmt.Sprintf("tenant-%d", n),
+	}
+}
+
+func testVRF(n int) *VRF {
+	return &VRF{
+		ID:            n,
+		Name:          fmt.Sprintf("VRF %d", n),
+		RD:            fmt.Sprintf("vrf-%d", n),
+		EnforceUnique: true,
+		Description:   fmt.Sprintf("VRF %d Description", n),
+		Tenant:        testTenantWithGroup(n, nil),
+	}
+}
+
+func testVRFCreate(n int) *VRF {
+	return &VRF{
+		Name:          fmt.Sprintf("VRF %d", n),
+		RD:            fmt.Sprintf("vrf-%d", n),
+		EnforceUnique: true,
+		Description:   fmt.Sprintf("VRF %d Description", n),
+		Tenant:        testTenantWithGroup(n, nil),
+	}
+}
+
+func testVRFWithTenant(n int, tenant *Tenant) *VRF {
+	return &VRF{
+		ID:            n,
+		Name:          fmt.Sprintf("VRF %d", n),
+		RD:            fmt.Sprintf("vrf-%d", n),
+		EnforceUnique: true,
+		Description:   fmt.Sprintf("VRF %d Description", n),
+		Tenant:        tenant,
+	}
+}
diff --git a/netbox/ipam.go b/netbox/ipam.go
index c61d3fc77a8a4a4b3d9c34bba1bde735680543da..a5abb11e23acac08735f1e81aa7c3466d9e478e8 100644
--- a/netbox/ipam.go
+++ b/netbox/ipam.go
@@ -16,5 +16,16 @@ package netbox
 
 // An IPAMService is used in a Client to access NetBox's IPAM API methods.
 type IPAMService struct {
-	c *Client
+	c    *Client
+	VRFs *VRFsService
+}
+
+// NewIPAMService returns a IPAMService initialized with all sub-services.
+func NewIPAMService(client *Client) *IPAMService {
+	return &IPAMService{
+		c: client,
+		VRFs: &VRFsService{
+			c: client,
+		},
+	}
 }
diff --git a/netbox/ipam_vrfs.go b/netbox/ipam_vrfs.go
new file mode 100644
index 0000000000000000000000000000000000000000..7524da87f9300d86ea6cacc7e91da5276d1de0d1
--- /dev/null
+++ b/netbox/ipam_vrfs.go
@@ -0,0 +1,118 @@
+// Copyright 2017 The go-netbox Authors.
+//
+// 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.
+
+// Code generated by generate_functions.go. DO NOT EDIT.
+
+package netbox
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+)
+
+// VRFsService is used in a Client to access NetBox's ipam/vrfs API methods.
+type VRFsService struct {
+	c *Client
+}
+
+// Get retrieves an VRF object from NetBox by its ID.
+func (s *VRFsService) Get(id int) (*VRF, error) {
+	req, err := s.c.NewRequest(
+		http.MethodGet,
+		fmt.Sprintf("api/ipam/vrfs/%d/", id),
+		nil,
+	)
+	if err != nil {
+		return nil, err
+	}
+
+	t := new(VRF)
+	err = s.c.Do(req, t)
+	if err != nil {
+		return nil, err
+	}
+	return t, nil
+}
+
+// List returns a Page associated with an NetBox API Endpoint.
+func (s *VRFsService) List(options *ListVRFOptions) *Page {
+	return NewPage(s.c, "api/ipam/vrfs/", options)
+}
+
+// Extract retrives a list of VRF objects from page.
+func (s *VRFsService) Extract(page *Page) ([]*VRF, error) {
+	if err := page.Err(); err != nil {
+		return nil, err
+	}
+
+	var groups []*VRF
+	if err := json.Unmarshal(page.data.Results, &groups); err != nil {
+		return nil, err
+	}
+	return groups, nil
+}
+
+// Create creates a new VRF object in NetBox and returns the ID of the new object.
+func (s *VRFsService) Create(data *VRF) (int, error) {
+	req, err := s.c.NewJSONRequest(http.MethodPost, "api/ipam/vrfs/", nil, data)
+	if err != nil {
+		return 0, err
+	}
+
+	g := new(writableVRF)
+	err = s.c.Do(req, g)
+	if err != nil {
+		return 0, err
+	}
+	return g.ID, nil
+}
+
+// Update changes an existing VRF object in NetBox, and returns the ID of the new object.
+func (s *VRFsService) Update(data *VRF) (int, error) {
+	req, err := s.c.NewJSONRequest(
+		http.MethodPatch,
+		fmt.Sprintf("api/ipam/vrfs/%d/", data.ID),
+		nil,
+		data,
+	)
+	if err != nil {
+		return 0, err
+	}
+
+	// g is just used to verify correct api result.
+	// data is not changed, because the g is not the full representation that one would
+	// get with Get. But if the response was unmarshaled into writableVRF correctly,
+	// everything went fine, and we do not need to update data.
+	g := new(writableVRF)
+	err = s.c.Do(req, g)
+	if err != nil {
+		return 0, err
+	}
+	return g.ID, nil
+}
+
+// Delete deletes an existing VRF object from NetBox.
+func (s *VRFsService) Delete(data *VRF) error {
+	req, err := s.c.NewRequest(
+		http.MethodDelete,
+		fmt.Sprintf("api/ipam/vrfs/%d/", data.ID),
+		nil,
+	)
+	if err != nil {
+		return err
+	}
+
+	return s.c.Do(req, nil)
+}
diff --git a/netbox/ipam_vrfs_basic_test.go b/netbox/ipam_vrfs_basic_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..101530e60a90cc9b54b7a6b2083ea1fe175d9fa1
--- /dev/null
+++ b/netbox/ipam_vrfs_basic_test.go
@@ -0,0 +1,299 @@
+// Copyright 2017 The go-netbox Authors.
+//
+// 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.
+
+// Code generated by generate_basic_tests.go. DO NOT EDIT.
+
+package netbox
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"net/http"
+	"reflect"
+	"testing"
+)
+
+// Using this to override MarshalJSON
+// In all cases when posting data to netbox-API, the VRF.MarshalJSON is what you want,
+// but not here as a return in testHandler
+type serverDataVRF VRF
+
+func convertToServerDataVRF(data []*VRF) []*serverDataVRF {
+	dataWant := make([]*serverDataVRF, len(data))
+	for i := range data {
+		tmp := serverDataVRF(*data[i])
+		dataWant[i] = &tmp
+	}
+	return dataWant
+}
+
+func TestBasicVRFGet(t *testing.T) {
+	var tests = []struct {
+		desc string
+		want *VRF
+	}{
+		{
+			desc: "Simple VRF",
+			want: testVRF(1),
+		},
+	}
+
+	for i, tt := range tests {
+		t.Run(fmt.Sprintf("[%d] %s", i, tt.desc), func(t *testing.T) {
+			serverData := serverDataVRF(*tt.want)
+
+			c, done := testClient(t, testHandler(t, http.MethodGet, "/api/ipam/vrfs/1/", &serverData))
+			defer done()
+
+			res, err := c.IPAM.VRFs.Get(1)
+			if err != nil {
+				t.Fatalf("unexpected error from Client.IPAM.VRFs.Get: %v", err)
+			}
+
+			if want, got := tt.want, res; !reflect.DeepEqual(want, got) {
+				t.Fatalf("unexpected VRF:\n- want: %v\n-  got: %v", want, got)
+			}
+		})
+	}
+}
+
+func TestBasicVRFGet404(t *testing.T) {
+	c, done := testClient(t, testStatusHandler(t, http.MethodGet, "/api/ipam/vrfs/1/", &struct {
+		Detail string `json:"detail"`
+	}{
+		Detail: "Not found.",
+	},
+		http.StatusNotFound))
+	defer done()
+
+	res, err := c.IPAM.VRFs.Get(1)
+	errstr := "404 - Not found."
+	if want, got := errors.New(errstr), err; !reflect.DeepEqual(want, got) {
+		t.Fatalf("unexpected error from Client.IPAM.VRFs.Get:\n- want: %v\n-  got: %v", want, got)
+	}
+
+	if res != nil {
+		t.Fatalf("unexpected result:\n- want: %v\n-  got: %v", nil, res)
+	}
+}
+
+func TestBasicListExtractVRF(t *testing.T) {
+	want := []*VRF{
+		testVRF(1),
+		testVRF(2),
+	}
+	serverWant := convertToServerDataVRF(want)
+	serverData, _ := json.Marshal(serverWant)
+	c, done := testClient(t, testHandler(t, http.MethodGet, "/api/ipam/vrfs/", &pageData{
+		Count:       2,
+		NextURL:     "",
+		PreviousURL: "",
+		Results:     serverData,
+	}))
+	defer done()
+
+	page := c.IPAM.VRFs.List(nil)
+
+	if page == nil {
+		t.Fatalf("unexpexted result from c.IPAM.VRFs.List.")
+	}
+
+	got := []*VRF{}
+	counter := 0
+	for page.Next() {
+		var err error
+		got, err = c.IPAM.VRFs.Extract(page)
+		if err != nil {
+			t.Fatalf("unexpected error from c.IPAM.VRFs.Extract: %v", err)
+		}
+		counter = counter + 1
+		if counter > 2 { // Safe guard
+			break
+		}
+	}
+	if counter != 1 {
+		t.Fatalf("unexpected page count:\n- want: 1\n-  got: %d", counter)
+	}
+
+	if !reflect.DeepEqual(want, got) {
+		t.Fatalf("unexpected result:\n- want: %v\n-  got: %v", want, got)
+	}
+
+	if page.Err() != nil {
+		t.Fatalf("unexpected error from page:\n- want: %v\n-  got: %v", want, got)
+	}
+}
+
+func TestBasicCreateVRF(t *testing.T) {
+	var tests = []struct {
+		desc       string
+		data       *VRF
+		want       int
+		serverData interface{}
+		status     int
+		errstr     string
+	}{
+		{
+			desc:       "Create with ID 0",
+			data:       testVRFCreate(1),
+			want:       1,
+			status:     0,
+			errstr:     "",
+			serverData: testVRF(1),
+		},
+		{
+			desc:   "Create duplicate",
+			data:   testVRFCreate(1),
+			want:   0,
+			status: http.StatusBadRequest,
+			errstr: "400 - {\"name\":[\"IpamVRFService with this name already exists.\"]}\n",
+			serverData: &struct {
+				Name []string `json:"name"`
+			}{
+				Name: []string{"IpamVRFService with this name already exists."},
+			},
+		},
+	}
+
+	for i, tt := range tests {
+		t.Run(fmt.Sprintf("[%d] %s", i, tt.desc), func(t *testing.T) {
+			c, done := testClient(t, testStatusHandler(t, http.MethodPost, "/api/ipam/vrfs/", tt.serverData, tt.status))
+			defer done()
+
+			var terr error
+			if tt.errstr != "" {
+				terr = errors.New(tt.errstr) // Using errstr and initialize real err here, to satisfy golint
+			}
+
+			res, err := c.IPAM.VRFs.Create(tt.data)
+			if want, got := terr, err; !reflect.DeepEqual(want, got) {
+				t.Fatalf("unexpected error:\n- want: %v\n-  got: %v", want, got)
+			}
+			if want, got := tt.want, res; !reflect.DeepEqual(want, got) {
+				t.Fatalf("unexpected VRF:\n- want: %v\n-  got: %v", want, got)
+			}
+		})
+	}
+}
+
+func TestBasicUpdateVRF(t *testing.T) {
+	var tests = []struct {
+		desc       string
+		data       *VRF
+		want       int
+		serverData interface{}
+		status     int
+		errstr     string
+	}{
+		{
+			desc:       "Update with ID 1",
+			data:       testVRF(1),
+			want:       1,
+			serverData: testVRF(1),
+			status:     0,
+			errstr:     "",
+		},
+		{
+			desc: "Update not found",
+			data: testVRF(1),
+			want: 0,
+			serverData: &struct {
+				Detail string
+			}{
+				Detail: "Not found.",
+			},
+			status: http.StatusNotFound,
+			errstr: "404 - Not found.",
+		},
+		{
+			desc: "Update to duplicate",
+			data: testVRF(1),
+			want: 0,
+			serverData: &struct {
+				Name []string `json:"name"`
+			}{
+				Name: []string{"IpamVRFService with this name already exists."},
+			},
+			status: http.StatusBadRequest,
+			errstr: "400 - {\"name\":[\"IpamVRFService with this name already exists.\"]}\n",
+		},
+	}
+
+	for i, tt := range tests {
+		t.Run(fmt.Sprintf("[%d] %s", i, tt.desc), func(t *testing.T) {
+			c, done := testClient(t, testStatusHandler(t, http.MethodPatch, "/api/ipam/vrfs/1/", tt.serverData, tt.status))
+			defer done()
+
+			var terr error
+			if tt.errstr != "" {
+				terr = errors.New(tt.errstr) // Using errstr and initialize real err here, to satisfy golint
+			}
+
+			res, err := c.IPAM.VRFs.Update(tt.data)
+			if want, got := terr, err; !reflect.DeepEqual(want, got) {
+				t.Fatalf("unexpected error:\n- want: %v\n-  got: %v", want, got)
+			}
+			if want, got := tt.want, res; !reflect.DeepEqual(want, got) {
+				t.Fatalf("unexpected VRF:\n- want: %v\n-  got: %v", want, got)
+			}
+		})
+	}
+}
+
+func TestBasicDeleteVRF(t *testing.T) {
+	var tests = []struct {
+		desc       string
+		data       *VRF
+		serverData interface{}
+		status     int
+		errstr     string
+	}{
+		{
+			desc:       "Delete ID 1",
+			data:       testVRF(1),
+			serverData: testVRF(1),
+			status:     0,
+			errstr:     "",
+		},
+		{
+			desc: "Delete not Found",
+			data: testVRF(1),
+			serverData: &struct {
+				Detail string `json:"detail"`
+			}{
+				Detail: "Not found.",
+			},
+			status: http.StatusNotFound,
+			errstr: "404 - Not found.",
+		},
+	}
+
+	for i, tt := range tests {
+		t.Run(fmt.Sprintf("[%d] %s", i, tt.desc), func(t *testing.T) {
+			c, done := testClient(t, testStatusHandler(t, http.MethodDelete, "/api/ipam/vrfs/1/", tt.serverData, tt.status))
+			defer done()
+
+			var terr error
+			if tt.errstr != "" {
+				terr = errors.New(tt.errstr) // Using errstr and initialize real err here, to satisfy golint
+			}
+
+			err := c.IPAM.VRFs.Delete(tt.data)
+			if want, got := terr, err; !reflect.DeepEqual(want, got) {
+				t.Fatalf("unexpected error:\n- want: %v\n-  got: %v", want, got)
+			}
+		})
+	}
+}
diff --git a/netbox/ipam_vrfs_test.go b/netbox/ipam_vrfs_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..3d00c622c2ffa1128e72f77256e7cc05e0c7e40e
--- /dev/null
+++ b/netbox/ipam_vrfs_test.go
@@ -0,0 +1,217 @@
+// Copyright 2017 The go-netbox Authors.
+//
+// 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.
+
+package netbox
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"net/url"
+	"reflect"
+	"testing"
+)
+
+func TestVRFGet(t *testing.T) {
+	var tests = []struct {
+		desc string
+		want *VRF
+	}{
+		{
+			desc: "Without Tenant",
+			want: testVRFWithTenant(1, nil),
+		},
+		{
+			desc: "With Tenant",
+			want: testVRFWithTenant(1, testTenantWithGroup(1, nil)),
+		},
+	}
+
+	for i, tt := range tests {
+		t.Run(fmt.Sprintf("[%d] %s", i, tt.desc), func(t *testing.T) {
+			serverData := serverDataVRF(*tt.want)
+
+			c, done := testClient(t, testHandler(t, http.MethodGet, "/api/ipam/vrfs/1/", &serverData))
+			defer done()
+
+			res, err := c.IPAM.VRFs.Get(1)
+			if err != nil {
+				t.Fatalf("unexpected error from Client.IPAM.VRFs.Get: %v", err)
+			}
+
+			if want, got := tt.want, res; !reflect.DeepEqual(want, got) {
+				t.Fatalf("unexpected Tenant:\n- want: %v\n-  got: %v", want, got)
+			}
+		})
+	}
+}
+
+func TestVRFUnmarshalJSON(t *testing.T) {
+	var tests = []struct {
+		desc string
+		data []byte
+		want *VRF
+	}{
+		{
+			desc: "Nil Group",
+			data: []byte(`{ "id": 1, "name": "VRF 1", "rd": "vrf-1", "tenant": null, "enforce_unique": true, "description": "VRF 1 Description", "custom_fields": {} }`),
+			want: testVRFWithTenant(1, nil),
+		},
+		{
+			desc: "With Group",
+			data: []byte(`{ "id": 1, "name": "VRF 1", "rd": "vrf-1", "tenant": { "id": 1, "name": "Tenant 1", "slug": "tenant-1" }, "enforce_unique": true, "description": "VRF 1 Description", "custom_fields": {} }`),
+			want: testVRFWithTenant(1, testTenantIdentifier(1)),
+		},
+	}
+
+	for i, tt := range tests {
+		t.Run(fmt.Sprintf("[%d] %s", i, tt.desc), func(t *testing.T) {
+			result := new(VRF)
+			err := json.Unmarshal(tt.data, result)
+			if err != nil {
+				t.Fatalf("unexpected error from Tenant.UnmarshalJSON: %v", err)
+			}
+
+			if want, got := tt.want, result; !reflect.DeepEqual(want, got) {
+				t.Fatalf("unexpected Tenant:\n- want: %v\n-  got: %v", want, got)
+			}
+		})
+	}
+}
+
+func TestVRFMarshalJSON(t *testing.T) {
+	var tests = []struct {
+		desc string
+		data *VRF
+		want []byte
+	}{
+		{
+			desc: "Nil Group",
+			data: testVRFWithTenant(1, nil),
+			want: []byte(`{"id":1,"name":"VRF 1","rd":"vrf-1","enforce_unique":true,"description":"VRF 1 Description"}`),
+		},
+		{
+			desc: "With Group",
+			data: testVRFWithTenant(1, testTenantIdentifier(1)),
+			want: []byte(`{"id":1,"name":"VRF 1","rd":"vrf-1","enforce_unique":true,"description":"VRF 1 Description","tenant":1}`),
+		},
+		{
+			desc: "No VRF.ID",
+			data: testVRFWithTenant(0, nil),
+			want: []byte(`{"name":"VRF 0","rd":"vrf-0","enforce_unique":true,"description":"VRF 0 Description"}`),
+		},
+	}
+
+	for i, tt := range tests {
+		t.Run(fmt.Sprintf("[%d] %s", i, tt.desc), func(t *testing.T) {
+			result, err := json.Marshal(tt.data)
+			if err != nil {
+				t.Fatalf("unexpected error from VRF.MarshalJSON: %v", err)
+			}
+
+			if want, got := tt.want, result; bytes.Compare(want, got) != 0 {
+				t.Fatalf("unexpected VRF:\n- want: %s\n-  got: %s", want, got)
+			}
+		})
+	}
+}
+
+func TestListVRFOptions(t *testing.T) {
+	enforceUniqueTrue := true
+	enforceUniqueFalse := false
+	var tests = []struct {
+		desc string
+		o    *ListVRFOptions
+		v    url.Values
+	}{
+		{
+			desc: "empty options",
+		},
+		{
+			desc: "full options",
+			o: &ListVRFOptions{
+				Name:          "Hello",
+				RD:            "hello",
+				EnforceUnique: &enforceUniqueTrue,
+				IDIn:          []uint64{1, 2, 3},
+				TenantID:      1,
+				Query:         "World",
+			},
+			v: url.Values{
+				"name":           []string{"Hello"},
+				"rd":             []string{"hello"},
+				"id__in":         []string{"1,2,3"},
+				"enforce_unique": []string{"True"},
+				"tenant_id":      []string{"1"},
+				"q":              []string{"World"},
+			},
+		},
+		{
+			desc: "tenant vs tenant_id",
+			o: &ListVRFOptions{
+				TenantID: 1,
+				Tenant:   "Tenant 1",
+			},
+			v: url.Values{
+				"tenant_id": []string{"1"},
+			},
+		},
+		{
+			desc: "tenant name",
+			o: &ListVRFOptions{
+				Tenant: "Tenant 1",
+			},
+			v: url.Values{
+				"tenant": []string{"Tenant 1"},
+			},
+		},
+		{
+			desc: "enforce_unique default value",
+			o:    &ListVRFOptions{},
+			v:    url.Values{},
+		},
+		{
+			desc: "enforce_unique true",
+			o: &ListVRFOptions{
+				EnforceUnique: &enforceUniqueTrue,
+			},
+			v: url.Values{
+				"enforce_unique": []string{"True"},
+			},
+		},
+		{
+			desc: "enforce_unique false",
+			o: &ListVRFOptions{
+				EnforceUnique: &enforceUniqueFalse,
+			},
+			v: url.Values{
+				"enforce_unique": []string{"False"},
+			},
+		},
+	}
+
+	for i, tt := range tests {
+		t.Run(fmt.Sprintf("[%d] %s", i, tt.desc), func(t *testing.T) {
+			v, err := tt.o.Values()
+			if err != nil {
+				t.Fatalf("unexpected Values error: %v", err)
+			}
+
+			if want, got := tt.v, v; !reflect.DeepEqual(want, got) {
+				t.Fatalf("unexpected url.Values map:\n- want: %v\n-  got: %v", want, got)
+			}
+		})
+	}
+}
diff --git a/netbox/ipam_vrfs_types.go b/netbox/ipam_vrfs_types.go
new file mode 100644
index 0000000000000000000000000000000000000000..f699efc851cedb6a57d29c7cd181db976594a602
--- /dev/null
+++ b/netbox/ipam_vrfs_types.go
@@ -0,0 +1,121 @@
+// Copyright 2017 The go-netbox Authors.
+//
+// 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.
+
+package netbox
+
+import (
+	"encoding/json"
+	"net/url"
+	"strconv"
+	"strings"
+)
+
+// A VRF is a representation of netbox vrf
+type VRF struct {
+	ID            int     `json:"id,omitempty"`
+	Name          string  `json:"name"`
+	RD            string  `json:"rd"`
+	EnforceUnique bool    `json:"enforce_unique"`
+	Description   string  `json:"description"`
+	Tenant        *Tenant `json:"tenant,omitempty"`
+}
+
+// A writableVRF corresponds to the Netbox API's
+// writable serializer for an VRF. It is used transparently
+// when VRF are serialized in to JSON.
+type writableVRF struct {
+	ID            int    `json:"id,omitempty"`
+	Name          string `json:"name"`
+	RD            string `json:"rd"`
+	EnforceUnique bool   `json:"enforce_unique"`
+	Description   string `json:"description"`
+	Tenant        *int   `json:"tenant,omitempty"`
+}
+
+// MarshalJSON marshals an VRF into JSON bytes,
+// and is used by the standard json package.
+func (v *VRF) MarshalJSON() ([]byte, error) {
+	var tenantID *int
+	if v.Tenant != nil {
+		tenantID = &v.Tenant.ID
+	}
+	return json.Marshal(writableVRF{
+		ID:            v.ID,
+		Name:          v.Name,
+		RD:            v.RD,
+		EnforceUnique: v.EnforceUnique,
+		Description:   v.Description,
+		Tenant:        tenantID,
+	})
+}
+
+// ListVRFOptions is used as an argument for Client.IPAM.VRFs.List.
+type ListVRFOptions struct {
+	Name          string
+	RD            string
+	EnforceUnique *bool
+	IDIn          []uint64
+	TenantID      int
+	Tenant        string
+	Query         string
+}
+
+// Values generates a url.Values map from the data in ListVRFOptions.
+func (o *ListVRFOptions) Values() (url.Values, error) {
+	if o == nil {
+		return nil, nil
+	}
+
+	v := url.Values{}
+
+	if o.Name != "" {
+		v.Set("name", o.Name)
+	}
+
+	if o.RD != "" {
+		v.Set("rd", o.RD)
+	}
+
+	if o.EnforceUnique != nil {
+		if *o.EnforceUnique {
+			v.Set("enforce_unique", "True")
+		} else {
+			v.Set("enforce_unique", "False")
+		}
+	}
+
+	if len(o.IDIn) > 0 {
+		vals := make([]string, len(o.IDIn))
+		for i, k := range o.IDIn {
+			vals[i] = strconv.FormatUint(k, 10)
+		}
+		v.Set("id__in", strings.Join(vals, ","))
+	}
+
+	switch {
+	case o.TenantID != 0:
+		v.Set("tenant_id", strconv.Itoa(o.TenantID))
+	case o.Tenant != "":
+		v.Set("tenant", o.Tenant)
+	}
+
+	if o.Query != "" {
+		v.Set("q", o.Query)
+	}
+
+	return v, nil
+}
+
+//go:generate go run generate_functions.go -type-name VRF -update-type-name writableVRF -service-name VRFsService -endpoint ipam -service vrfs
+//go:generate go run generate_basic_tests.go -type-name VRF -service-name IpamVRFService -endpoint ipam -service vrfs -client-endpoint IPAM -client-service VRFs