diff --git a/pkg/api/v1alpha1/clusterctl_types.go b/pkg/api/v1alpha1/clusterctl_types.go
index a9a50e6d5..ca5b21300 100644
--- a/pkg/api/v1alpha1/clusterctl_types.go
+++ b/pkg/api/v1alpha1/clusterctl_types.go
@@ -16,15 +16,9 @@ package v1alpha1
 
 import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/runtime/schema"
 	clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
 )
 
-var (
-	// GroupVersionKind is group version used to register these objects
-	GroupVersionKind = schema.GroupVersionKind{Group: "airshipit.org", Version: "v1alpha1", Kind: "Clusterctl"}
-)
-
 // +kubebuilder:object:root=true
 
 // Clusterctl provides information about clusterctl components
diff --git a/pkg/clusterctl/cmd/command.go b/pkg/clusterctl/cmd/command.go
index 33cad7484..540bcbe49 100644
--- a/pkg/clusterctl/cmd/command.go
+++ b/pkg/clusterctl/cmd/command.go
@@ -15,8 +15,6 @@
 package cmd
 
 import (
-	"sigs.k8s.io/yaml"
-
 	airshipv1 "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
 	"opendev.org/airship/airshipctl/pkg/clusterctl/client"
 	"opendev.org/airship/airshipctl/pkg/config"
@@ -71,21 +69,22 @@ func (c *Command) Init() error {
 }
 
 func clusterctlOptions(bundle document.Bundle) (*airshipv1.Clusterctl, error) {
-	doc, err := bundle.SelectOne(document.NewClusterctlSelector())
+	cctl := &airshipv1.Clusterctl{}
+	selector, err := document.NewSelector().ByObject(cctl, airshipv1.Scheme)
 	if err != nil {
 		return nil, err
 	}
-	options := &airshipv1.Clusterctl{}
-	b, err := doc.AsYAML()
+
+	doc, err := bundle.SelectOne(selector)
 	if err != nil {
 		return nil, err
 	}
-	// TODO (kkalynovskyi) instead of this, use kubernetes serializer
-	err = yaml.Unmarshal(b, options)
-	if err != nil {
+
+	if err := doc.ToAPIObject(cctl, airshipv1.Scheme); err != nil {
 		return nil, err
 	}
-	return options, nil
+
+	return cctl, nil
 }
 
 func getBundle(conf *config.Config) (document.Bundle, error) {
diff --git a/pkg/document/document.go b/pkg/document/document.go
index bfa899f4f..5da9bba4f 100644
--- a/pkg/document/document.go
+++ b/pkg/document/document.go
@@ -15,6 +15,9 @@
 package document
 
 import (
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/serializer/json"
+
 	"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
 	"sigs.k8s.io/kustomize/api/resource"
 	"sigs.k8s.io/yaml"
@@ -47,6 +50,7 @@ type Document interface {
 	Label(map[string]string)
 	MarshalJSON() ([]byte, error)
 	ToObject(interface{}) error
+	ToAPIObject(runtime.Object, *runtime.Scheme) error
 }
 
 // Factory implements Document
@@ -188,6 +192,23 @@ func (d *Factory) ToObject(obj interface{}) error {
 	return yaml.Unmarshal(docYAML, obj)
 }
 
+// ToAPIObject de-serializes a document into a runtime.Object
+func (d *Factory) ToAPIObject(obj runtime.Object, scheme *runtime.Scheme) error {
+	y, err := d.AsYAML()
+	if err != nil {
+		return err
+	}
+
+	yamlSerializer := json.NewSerializerWithOptions(
+		json.DefaultMetaFactory,
+		scheme,
+		scheme,
+		json.SerializerOptions{Yaml: true, Pretty: false, Strict: false})
+
+	_, _, err = yamlSerializer.Decode(y, nil, obj)
+	return err
+}
+
 // NewDocument is a convenience method to construct a new Document.  Although
 // an error is unlikely at this time, this provides some future proofing for
 // when we want more strict airship specific validation of documents getting
diff --git a/pkg/document/document_test.go b/pkg/document/document_test.go
index f18967ef3..848512c95 100644
--- a/pkg/document/document_test.go
+++ b/pkg/document/document_test.go
@@ -21,6 +21,9 @@ import (
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	airapiv1 "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
 	"opendev.org/airship/airshipctl/pkg/document"
 	"opendev.org/airship/airshipctl/testutil"
 )
@@ -102,6 +105,33 @@ func TestDocument(t *testing.T) {
 		assert.Equal(expectedObj, actualObj)
 	})
 
+	t.Run("ToAPIObject", func(t *testing.T) {
+		expectedObj := &airapiv1.Clusterctl{
+			TypeMeta: metav1.TypeMeta{
+				Kind:       "Clusterctl",
+				APIVersion: "airshipit.org/v1alpha1",
+			},
+			ObjectMeta: metav1.ObjectMeta{
+				Name: "clusterctl-v1",
+			},
+			Providers: []*airapiv1.Provider{
+				{
+					Name: "aws",
+					Type: "InfrastructureProvider",
+					URL:  "/manifests/capi/infra/aws/v0.3.0",
+				},
+			},
+		}
+		sel, err := document.NewSelector().ByObject(expectedObj, airapiv1.Scheme)
+		require.NoError(err)
+		doc, err := bundle.SelectOne(sel)
+		require.NoError(err)
+		actualObj := &airapiv1.Clusterctl{}
+		err = doc.ToAPIObject(actualObj, airapiv1.Scheme)
+		assert.NoError(err)
+		assert.Equal(expectedObj, actualObj)
+	})
+
 	t.Run("GetString", func(t *testing.T) {
 		doc, err := bundle.GetByName("some-random-deployment-we-will-filter")
 		require.NoError(err, "Unexpected error trying to GetByName")
diff --git a/pkg/document/errors.go b/pkg/document/errors.go
index b7c7e2ff6..70cb0c789 100644
--- a/pkg/document/errors.go
+++ b/pkg/document/errors.go
@@ -16,6 +16,8 @@ package document
 
 import (
 	"fmt"
+
+	"k8s.io/apimachinery/pkg/runtime"
 )
 
 // ErrDocNotFound returned if desired document not found by selector
@@ -41,6 +43,12 @@ type ErrDocumentMalformed struct {
 	Message string
 }
 
+// ErrRuntimeObjectKind returned if runtime object contains either none or
+// more than one Kinds defined in schema
+type ErrRuntimeObjectKind struct {
+	Obj runtime.Object
+}
+
 func (e ErrDocNotFound) Error() string {
 	return fmt.Sprintf("document filtered by selector %v found no documents", e.Selector)
 }
@@ -56,3 +64,7 @@ func (e ErrDocumentDataKeyNotFound) Error() string {
 func (e ErrDocumentMalformed) Error() string {
 	return fmt.Sprintf("document %q is malformed: %q", e.DocName, e.Message)
 }
+
+func (e ErrRuntimeObjectKind) Error() string {
+	return fmt.Sprintf("object %#v has either none or multiple kinds in scheme (expected one)", e.Obj)
+}
diff --git a/pkg/document/selectors.go b/pkg/document/selectors.go
index 0d1132969..e46d10773 100644
--- a/pkg/document/selectors.go
+++ b/pkg/document/selectors.go
@@ -18,10 +18,11 @@ import (
 	"fmt"
 	"strings"
 
+	"k8s.io/apimachinery/pkg/api/meta"
+	"k8s.io/apimachinery/pkg/runtime"
+
 	"sigs.k8s.io/kustomize/api/resid"
 	"sigs.k8s.io/kustomize/api/types"
-
-	airshipv1 "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
 )
 
 // Selector provides abstraction layer in front of kustomize selector
@@ -81,6 +82,28 @@ func (s Selector) ByAnnotation(annotationSelector string) Selector {
 	return s
 }
 
+// ByObject select by runtime object defined in API schema
+func (s Selector) ByObject(obj runtime.Object, scheme *runtime.Scheme) (Selector, error) {
+	gvks, _, err := scheme.ObjectKinds(obj)
+	if err != nil {
+		return Selector{}, err
+	}
+
+	if len(gvks) != 1 {
+		return Selector{}, ErrRuntimeObjectKind{Obj: obj}
+	}
+	result := NewSelector().ByGvk(gvks[0].Group, gvks[0].Version, gvks[0].Kind)
+
+	accessor, err := meta.Accessor(obj)
+	if err != nil {
+		return Selector{}, err
+	}
+	if name := accessor.GetName(); name != "" {
+		result = result.ByName(name)
+	}
+	return result, nil
+}
+
 // String is a convenience function which dumps all relevant information about a Selector in the following format:
 // [Key1=Value1, Key2=Value2, ...]
 func (s Selector) String() string {
@@ -166,12 +189,3 @@ func NewClusterctlMetadataSelector() Selector {
 		ClusterctlMetadataVersion,
 		ClusterctlMetadataKind)
 }
-
-// NewClusterctlSelector returns a selector to get document that controls how clusterctl
-// components will be applied
-func NewClusterctlSelector() Selector {
-	return NewSelector().ByGvk(
-		airshipv1.GroupVersionKind.Group,
-		airshipv1.GroupVersionKind.Version,
-		airshipv1.GroupVersionKind.Kind)
-}
diff --git a/pkg/document/selectors_test.go b/pkg/document/selectors_test.go
index 060227773..f87a91ed1 100644
--- a/pkg/document/selectors_test.go
+++ b/pkg/document/selectors_test.go
@@ -19,7 +19,13 @@ import (
 
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
+	k8sv1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"sigs.k8s.io/kustomize/api/resid"
+	"sigs.k8s.io/kustomize/api/types"
 
+	airapiv1 "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
 	"opendev.org/airship/airshipctl/pkg/document"
 	"opendev.org/airship/airshipctl/testutil"
 )
@@ -54,12 +60,6 @@ func TestSelectorsPositive(t *testing.T) {
 		require.NoError(t, err)
 		assert.Len(t, doc, 1)
 	})
-
-	t.Run("TestNewClusterctlSelector", func(t *testing.T) {
-		docs, err := bundle.Select(document.NewClusterctlSelector())
-		require.NoError(t, err)
-		assert.Len(t, docs, 1)
-	})
 }
 
 func TestSelectorsNegative(t *testing.T) {
@@ -142,3 +142,65 @@ func TestSelectorString(t *testing.T) {
 		})
 	}
 }
+
+func TestSelectorToObject(t *testing.T) {
+	tests := []struct {
+		name        string
+		obj         runtime.Object
+		expectedSel document.Selector
+		expectedErr string
+	}{
+		{
+			name: "Selector with GVK",
+			obj:  &airapiv1.Clusterctl{},
+			expectedSel: document.Selector{
+				Selector: types.Selector{
+					Gvk: resid.Gvk{
+						Group:   "airshipit.org",
+						Version: "v1alpha1",
+						Kind:    "Clusterctl",
+					},
+				},
+			},
+			expectedErr: "",
+		},
+		{
+			name:        "Unregistered object",
+			obj:         &k8sv1.Pod{},
+			expectedSel: document.Selector{},
+			expectedErr: "no kind is registered for the type v1.Pod in scheme",
+		},
+		{
+			name: "Selector with GVK and Name",
+			obj: &airapiv1.Clusterctl{
+				ObjectMeta: metav1.ObjectMeta{
+					Name: "clusterctl-v1",
+				},
+			},
+			expectedSel: document.Selector{
+				Selector: types.Selector{
+					Gvk: resid.Gvk{
+						Group:   "airshipit.org",
+						Version: "v1alpha1",
+						Kind:    "Clusterctl",
+					},
+					Name: "clusterctl-v1",
+				},
+			},
+			expectedErr: "",
+		},
+	}
+	for _, test := range tests {
+		tt := test
+		t.Run(tt.name, func(t *testing.T) {
+			actualSel, err := document.NewSelector().
+				ByObject(tt.obj, airapiv1.Scheme)
+			if test.expectedErr != "" {
+				assert.Contains(t, err.Error(), tt.expectedErr)
+			} else {
+				require.NoError(t, err)
+				assert.Equal(t, tt.expectedSel, actualSel)
+			}
+		})
+	}
+}
diff --git a/pkg/document/testdata/common/clusterctl.yaml b/pkg/document/testdata/common/clusterctl.yaml
new file mode 100644
index 000000000..51a7afe7e
--- /dev/null
+++ b/pkg/document/testdata/common/clusterctl.yaml
@@ -0,0 +1,9 @@
+---
+apiVersion: airshipit.org/v1alpha1
+kind: Clusterctl
+metadata:
+  name: clusterctl-v1
+providers:
+- name: "aws"
+  type: "InfrastructureProvider"
+  url: "/manifests/capi/infra/aws/v0.3.0"
\ No newline at end of file
diff --git a/pkg/document/testdata/common/kustomization.yaml b/pkg/document/testdata/common/kustomization.yaml
index 185967316..9bac53546 100644
--- a/pkg/document/testdata/common/kustomization.yaml
+++ b/pkg/document/testdata/common/kustomization.yaml
@@ -3,4 +3,5 @@ resources:
  - tiller.yaml
  - argo.yaml
  - initially_ignored.yaml
- - custom_resource.yaml
\ No newline at end of file
+ - custom_resource.yaml
+ - clusterctl.yaml
\ No newline at end of file