diff --git a/cmd/cluster/cluster.go b/cmd/cluster/cluster.go
index 36401a8ce..e49c1b8a4 100644
--- a/cmd/cluster/cluster.go
+++ b/cmd/cluster/cluster.go
@@ -18,6 +18,7 @@ import (
 	"github.com/spf13/cobra"
 
 	"opendev.org/airship/airshipctl/pkg/environment"
+	"opendev.org/airship/airshipctl/pkg/k8s/client"
 	"opendev.org/airship/airshipctl/pkg/log"
 )
 
@@ -45,6 +46,7 @@ func NewClusterCommand(rootSettings *environment.AirshipCTLSettings) *cobra.Comm
 
 	clusterRootCmd.AddCommand(NewInitCommand(rootSettings))
 	clusterRootCmd.AddCommand(NewMoveCommand(rootSettings))
+	clusterRootCmd.AddCommand(NewStatusCommand(rootSettings, client.DefaultClient))
 
 	return clusterRootCmd
 }
diff --git a/cmd/cluster/status.go b/cmd/cluster/status.go
new file mode 100644
index 000000000..ef77cd211
--- /dev/null
+++ b/cmd/cluster/status.go
@@ -0,0 +1,82 @@
+/*
+ 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
+
+     https://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 cluster
+
+import (
+	"fmt"
+
+	"github.com/spf13/cobra"
+
+	"opendev.org/airship/airshipctl/pkg/cluster"
+	"opendev.org/airship/airshipctl/pkg/document"
+	"opendev.org/airship/airshipctl/pkg/environment"
+	"opendev.org/airship/airshipctl/pkg/k8s/client"
+	"opendev.org/airship/airshipctl/pkg/log"
+	"opendev.org/airship/airshipctl/pkg/util"
+)
+
+// NewStatusCommand creates a command which reports the statuses of a cluster's deployed components.
+func NewStatusCommand(rootSettings *environment.AirshipCTLSettings, factory client.Factory) *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "status",
+		Short: "Retrieve statuses of deployed cluster components",
+		RunE: func(cmd *cobra.Command, args []string) error {
+			conf := rootSettings.Config
+			if err := conf.EnsureComplete(); err != nil {
+				return err
+			}
+
+			manifest, err := conf.CurrentContextManifest()
+			if err != nil {
+				return err
+			}
+
+			docBundle, err := document.NewBundleByPath(manifest.TargetPath)
+			if err != nil {
+				return err
+			}
+
+			docs, err := docBundle.GetAllDocuments()
+			if err != nil {
+				return err
+			}
+
+			client, err := factory(rootSettings)
+			if err != nil {
+				return err
+			}
+
+			statusMap, err := cluster.NewStatusMap(client)
+			if err != nil {
+				return err
+			}
+
+			tw := util.NewTabWriter(cmd.OutOrStdout())
+			fmt.Fprintf(tw, "Kind\tName\tStatus\n")
+			for _, doc := range docs {
+				status, err := statusMap.GetStatusForResource(doc)
+				if err != nil {
+					log.Debug(err)
+				} else {
+					fmt.Fprintf(tw, "%s\t%s\t%s\n", doc.GetKind(), doc.GetName(), status)
+				}
+			}
+			tw.Flush()
+			return nil
+		},
+	}
+
+	return cmd
+}
diff --git a/cmd/cluster/status_test.go b/cmd/cluster/status_test.go
new file mode 100644
index 000000000..3abb0b290
--- /dev/null
+++ b/cmd/cluster/status_test.go
@@ -0,0 +1,153 @@
+/*
+ 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
+
+     https://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 cluster_test
+
+import (
+	"testing"
+
+	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	"k8s.io/apimachinery/pkg/runtime"
+
+	"opendev.org/airship/airshipctl/cmd/cluster"
+	"opendev.org/airship/airshipctl/pkg/config"
+	"opendev.org/airship/airshipctl/pkg/environment"
+	"opendev.org/airship/airshipctl/pkg/k8s/client"
+	"opendev.org/airship/airshipctl/pkg/k8s/client/fake"
+	"opendev.org/airship/airshipctl/testutil"
+)
+
+const (
+	fixturesPath = "testdata/statusmap"
+)
+
+func TestStatusCmd(t *testing.T) {
+	tests := []struct {
+		cmdTest   *testutil.CmdTest
+		resources []runtime.Object
+		CRDs      []runtime.Object
+	}{
+		{
+			cmdTest: &testutil.CmdTest{
+				Name:    "check-status-no-resources",
+				CmdLine: "",
+			},
+		},
+		{
+			cmdTest: &testutil.CmdTest{
+				Name:    "check-status-with-resources",
+				CmdLine: "",
+			},
+			resources: []runtime.Object{
+				makeResource("Resource", "stable-resource", "stable"),
+				makeResource("Resource", "pending-resource", "pending"),
+			},
+			CRDs: []runtime.Object{
+				makeResourceCRD(annotationValidStatusCheck()),
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		tt := tt
+		testClientFactory := func(_ *environment.AirshipCTLSettings) (client.Interface, error) {
+			return fake.NewClient(
+				fake.WithDynamicObjects(tt.resources...),
+				fake.WithCRDs(tt.CRDs...),
+			), nil
+		}
+		tt.cmdTest.Cmd = cluster.NewStatusCommand(clusterStatusTestSettings(), testClientFactory)
+		testutil.RunTest(t, tt.cmdTest)
+	}
+}
+
+func clusterStatusTestSettings() *environment.AirshipCTLSettings {
+	return &environment.AirshipCTLSettings{
+		Config: &config.Config{
+			Clusters:  map[string]*config.ClusterPurpose{"testCluster": nil},
+			AuthInfos: map[string]*config.AuthInfo{"testAuthInfo": nil},
+			Contexts: map[string]*config.Context{
+				"testContext": {Manifest: "testManifest"},
+			},
+			Manifests: map[string]*config.Manifest{
+				"testManifest": {TargetPath: fixturesPath},
+			},
+			CurrentContext: "testContext",
+		},
+	}
+}
+
+func makeResource(kind, name, state string) *unstructured.Unstructured {
+	return &unstructured.Unstructured{
+		Object: map[string]interface{}{
+			"apiVersion": "example.com/v1",
+			"kind":       kind,
+			"metadata": map[string]interface{}{
+				"name":      name,
+				"namespace": "default",
+			},
+			"status": map[string]interface{}{
+				"state": state,
+			},
+		},
+	}
+}
+
+func annotationValidStatusCheck() map[string]string {
+	return map[string]string{
+		"airshipit.org/status-check": `
+[
+  {
+    "status": "Stable",
+    "condition": "@.status.state==\"stable\""
+  },
+  {
+    "status": "Pending",
+    "condition": "@.status.state==\"pending\""
+  }
+]`,
+	}
+}
+
+func makeResourceCRD(annotations map[string]string) *apiextensionsv1.CustomResourceDefinition {
+	return &apiextensionsv1.CustomResourceDefinition{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       "CustomResourceDefinition",
+			APIVersion: "apiextensions.k8s.io/v1",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:        "resources.example.com",
+			Annotations: annotations,
+		},
+		Spec: apiextensionsv1.CustomResourceDefinitionSpec{
+			Group: "example.com",
+			Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
+				{
+					Name:    "v1",
+					Served:  true,
+					Storage: true,
+				},
+			},
+			// omitting the openAPIV3Schema for brevity
+			Scope: "Namespaced",
+			Names: apiextensionsv1.CustomResourceDefinitionNames{
+				Kind:     "Resource",
+				Plural:   "resources",
+				Singular: "resource",
+			},
+		},
+	}
+}
diff --git a/cmd/cluster/testdata/TestNewClusterCommandGoldenOutput/cluster-cmd-with-help.golden b/cmd/cluster/testdata/TestNewClusterCommandGoldenOutput/cluster-cmd-with-help.golden
index a365fa364..416509fc3 100644
--- a/cmd/cluster/testdata/TestNewClusterCommandGoldenOutput/cluster-cmd-with-help.golden
+++ b/cmd/cluster/testdata/TestNewClusterCommandGoldenOutput/cluster-cmd-with-help.golden
@@ -8,6 +8,7 @@ Available Commands:
   help        Help about any command
   init        Deploy cluster-api provider components
   move        Move Cluster API objects, provider specific objects and all dependencies to the target cluster
+  status      Retrieve statuses of deployed cluster components
 
 Flags:
   -h, --help   help for cluster
diff --git a/cmd/cluster/testdata/TestStatusCmdGoldenOutput/check-status-no-resources.golden b/cmd/cluster/testdata/TestStatusCmdGoldenOutput/check-status-no-resources.golden
new file mode 100644
index 000000000..c6c460a71
--- /dev/null
+++ b/cmd/cluster/testdata/TestStatusCmdGoldenOutput/check-status-no-resources.golden
@@ -0,0 +1 @@
+Kind   Name   Status
diff --git a/cmd/cluster/testdata/TestStatusCmdGoldenOutput/check-status-with-resources.golden b/cmd/cluster/testdata/TestStatusCmdGoldenOutput/check-status-with-resources.golden
new file mode 100644
index 000000000..54a9f0dbb
--- /dev/null
+++ b/cmd/cluster/testdata/TestStatusCmdGoldenOutput/check-status-with-resources.golden
@@ -0,0 +1,3 @@
+Kind       Name               Status
+Resource   pending-resource   Pending
+Resource   stable-resource    Stable
diff --git a/cmd/cluster/testdata/statusmap/crd.yaml b/cmd/cluster/testdata/statusmap/crd.yaml
new file mode 100644
index 000000000..929eeb969
--- /dev/null
+++ b/cmd/cluster/testdata/statusmap/crd.yaml
@@ -0,0 +1,40 @@
+# this CRD defines a type whose status can be checked using the condition in
+# the annotations
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  name: resources.example.com
+  annotations:
+    airshipit.org/status-check: |
+      [
+        {
+          "status": "Stable",
+          "condition": "@.status.state==\"stable\""
+        },
+        {
+          "status": "Pending",
+          "condition": "@.status.state==\"pending\""
+        }
+      ]
+spec:
+  group: example.com
+  versions:
+    - name: v1
+      served: true
+      storage: true
+      schema:
+        openAPIV3Schema:
+          type: object
+          properties:
+            status:
+              type: object
+              properties:
+                state:
+                  type: string
+  scope: Namespaced
+  names:
+    plural: resources
+    singular: resource
+    kind: Resource
+    shortNames:
+    - rsc
diff --git a/cmd/cluster/testdata/statusmap/kustomization.yaml b/cmd/cluster/testdata/statusmap/kustomization.yaml
new file mode 100644
index 000000000..f4b156e41
--- /dev/null
+++ b/cmd/cluster/testdata/statusmap/kustomization.yaml
@@ -0,0 +1,8 @@
+resources:
+  - crd.yaml
+  - stable-resource.yaml
+  - pending-resource.yaml
+  - missing.yaml
+  - unknown.yaml
+  - legacy-crd.yaml
+  - legacy-resource.yaml
diff --git a/cmd/cluster/testdata/statusmap/legacy-crd.yaml b/cmd/cluster/testdata/statusmap/legacy-crd.yaml
new file mode 100644
index 000000000..e88f547be
--- /dev/null
+++ b/cmd/cluster/testdata/statusmap/legacy-crd.yaml
@@ -0,0 +1,43 @@
+# this is a legacy CRD which defines a type whose status can be checked using
+# the condition in the annotations
+# It is included in tests to assure backward compatibility
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+metadata:
+  name: legacies.example.com
+  annotations:
+    airshipit.org/status-check: |
+      [
+        {
+          "status": "Stable",
+          "condition": "@.status.state==\"stable\""
+        },
+        {
+          "status": "Pending",
+          "condition": "@.status.state==\"pending\""
+        }
+      ]
+spec:
+  group: example.com
+  versions:
+    - name: v1
+      served: true
+      storage: true
+  scope: Namespaced
+  names:
+    plural: legacies
+    singular: legacy
+    kind: Legacy
+    shortNames:
+    - lgc
+  preserveUnknownFields: false
+  validation:
+    openAPIV3Schema:
+      type: object
+      properties:
+        status:
+          type: object
+          properties:
+            state:
+              type: string
diff --git a/cmd/cluster/testdata/statusmap/legacy-resource.yaml b/cmd/cluster/testdata/statusmap/legacy-resource.yaml
new file mode 100644
index 000000000..2aca6df17
--- /dev/null
+++ b/cmd/cluster/testdata/statusmap/legacy-resource.yaml
@@ -0,0 +1,7 @@
+# this legacy-resource is stable because the fake version in the cluster will
+# have .status.state == "stable"
+apiVersion: "example.com/v1"
+kind: Legacy
+metadata:
+  name: stable-legacy
+  namespace: default
diff --git a/cmd/cluster/testdata/statusmap/missing.yaml b/cmd/cluster/testdata/statusmap/missing.yaml
new file mode 100644
index 000000000..43729dd45
--- /dev/null
+++ b/cmd/cluster/testdata/statusmap/missing.yaml
@@ -0,0 +1,7 @@
+# This resource doesn't have a status-check defined by its CRD (which is also
+# missing for brevity). Requesting its status is an error
+apiVersion: "example.com/v1"
+kind: Missing
+metadata:
+  name: missing-resource
+  namespace: default
diff --git a/cmd/cluster/testdata/statusmap/pending-resource.yaml b/cmd/cluster/testdata/statusmap/pending-resource.yaml
new file mode 100644
index 000000000..b1373a07a
--- /dev/null
+++ b/cmd/cluster/testdata/statusmap/pending-resource.yaml
@@ -0,0 +1,7 @@
+# this resource is pending because the fake version in the cluster will
+# have .status.state == "pending"
+apiVersion: "example.com/v1"
+kind: Resource
+metadata:
+  name: pending-resource
+  namespace: default
diff --git a/cmd/cluster/testdata/statusmap/stable-resource.yaml b/cmd/cluster/testdata/statusmap/stable-resource.yaml
new file mode 100644
index 000000000..a6fff614f
--- /dev/null
+++ b/cmd/cluster/testdata/statusmap/stable-resource.yaml
@@ -0,0 +1,7 @@
+# this resource is stable because the fake version in the cluster will have
+# .status.state == "stable"
+apiVersion: "example.com/v1"
+kind: Resource
+metadata:
+  name: stable-resource
+  namespace: default
diff --git a/cmd/cluster/testdata/statusmap/unknown.yaml b/cmd/cluster/testdata/statusmap/unknown.yaml
new file mode 100644
index 000000000..58c65e17f
--- /dev/null
+++ b/cmd/cluster/testdata/statusmap/unknown.yaml
@@ -0,0 +1,8 @@
+# this resource is in an unknown state because the fake version in the cluster
+# will have .status.state == "unknown", which does not correlate to any of the
+# status checks in the CRD.
+apiVersion: "example.com/v1"
+kind: Resource
+metadata:
+  name: unknown
+  namespace: default
diff --git a/docs/source/cli/airshipctl_cluster.md b/docs/source/cli/airshipctl_cluster.md
index 2e761cc1f..f6ab19cda 100644
--- a/docs/source/cli/airshipctl_cluster.md
+++ b/docs/source/cli/airshipctl_cluster.md
@@ -27,4 +27,5 @@ such as getting status and deploying initial infrastructure.
 * [airshipctl](airshipctl.md)	 - A unified entrypoint to various airship components
 * [airshipctl cluster init](airshipctl_cluster_init.md)	 - Deploy cluster-api provider components
 * [airshipctl cluster move](airshipctl_cluster_move.md)	 - Move Cluster API objects, provider specific objects and all dependencies to the target cluster
+* [airshipctl cluster status](airshipctl_cluster_status.md)	 - Retrieve statuses of deployed cluster components
 
diff --git a/docs/source/cli/airshipctl_cluster_status.md b/docs/source/cli/airshipctl_cluster_status.md
new file mode 100644
index 000000000..547701c76
--- /dev/null
+++ b/docs/source/cli/airshipctl_cluster_status.md
@@ -0,0 +1,30 @@
+## airshipctl cluster status
+
+Retrieve statuses of deployed cluster components
+
+### Synopsis
+
+Retrieve statuses of deployed cluster components
+
+```
+airshipctl cluster status [flags]
+```
+
+### Options
+
+```
+  -h, --help   help for status
+```
+
+### Options inherited from parent commands
+
+```
+      --airshipconf string   Path to file for airshipctl configuration. (default "$HOME/.airship/config")
+      --debug                enable verbose output
+      --kubeconfig string    Path to kubeconfig associated with airshipctl configuration. (default "$HOME/.airship/kubeconfig")
+```
+
+### SEE ALSO
+
+* [airshipctl cluster](airshipctl_cluster.md)	 - Manage Kubernetes clusters
+
diff --git a/docs/source/cli/airshipctl_phase_render.md b/docs/source/cli/airshipctl_phase_render.md
index 17463b4f0..e21c269d3 100644
--- a/docs/source/cli/airshipctl_phase_render.md
+++ b/docs/source/cli/airshipctl_phase_render.md
@@ -14,12 +14,12 @@ airshipctl phase render PHASE_NAME [flags]
 
 ```
 
-#Get all 'initinfra' phase documents containing labels "app=helm" and
-#"service=tiller"
+# Get all 'initinfra' phase documents containing labels "app=helm" and
+# "service=tiller"
 airshipctl phase render initinfra -l app=helm,service=tiller
 
-#Get all documents containing labels "app=helm" and "service=tiller"
-#and kind 'Deployment'
+# Get all documents containing labels "app=helm" and "service=tiller"
+# and kind 'Deployment'
 airshipctl phase render initinfra -l app=helm,service=tiller -k Deployment
 
 ```