Repository: chef/automate
Pull request #3683 (2020-05-14 12:49:29)

Give chef server backup nightly test more time
We're hitting the 30 min limit


Patch:
diff --git a/.expeditor/nightly.pipeline.yml b/.expeditor/nightly.pipeline.yml
index 2bf8421f3..2bc469797 100644
--- a/.expeditor/nightly.pipeline.yml
+++ b/.expeditor/nightly.pipeline.yml
@@ -71,7 +71,7 @@ steps:
   - label: "[integration] chef server backup"
     command:
       - integration/run_test integration/tests/chef_server_backup.sh
-    timeout_in_minutes: 30 # longer timeout for chef-server
+    timeout_in_minutes: 40 # longer timeout for chef-server
     expeditor:
       executor:
         linux:
diff --git a/INSPEC_VERSION b/INSPEC_VERSION
index fde3e51c5..42c2334ec 100644
--- a/INSPEC_VERSION
+++ b/INSPEC_VERSION
@@ -1,1 +1,1 @@
-4.18.111
+4.18.114
diff --git a/api/config/dex/config_request.pb.go b/api/config/dex/config_request.pb.go
index 40c50a9a7..537f05ce7 100644
--- a/api/config/dex/config_request.pb.go
+++ b/api/config/dex/config_request.pb.go
@@ -953,6 +953,7 @@ type ConfigRequest_V1_Saml struct {
 	GroupsAttr           *wrappers.StringValue `protobuf:"bytes,5,opt,name=groups_attr,json=groupsAttr,proto3" json:"groups_attr,omitempty" toml:"groups_attr,omitempty" mapstructure:"groups_attr,omitempty"`
 	EntityIssuer         *wrappers.StringValue `protobuf:"bytes,6,opt,name=entity_issuer,json=entityIssuer,proto3" json:"entity_issuer,omitempty" toml:"entity_issuer,omitempty" mapstructure:"entity_issuer,omitempty"`
 	NameIdPolicyFormat   *wrappers.StringValue `protobuf:"bytes,7,opt,name=name_id_policy_format,json=nameIdPolicyFormat,proto3" json:"name_id_policy_format,omitempty" toml:"name_id_policy_format,omitempty" mapstructure:"name_id_policy_format,omitempty"`
+	AllowedGroups        []string              `protobuf:"bytes,8,rep,name=allowed_groups,json=allowedGroups,proto3" json:"allowed_groups,omitempty" toml:"allowed_groups,omitempty" mapstructure:"allowed_groups,omitempty"`
 	XXX_NoUnkeyedLiteral struct{}              `json:"-" toml:"-" mapstructure:"-,omitempty"`
 	XXX_unrecognized     []byte                `json:"-" toml:"-" mapstructure:"-,omitempty"`
 	XXX_sizecache        int32                 `json:"-" toml:"-" mapstructure:"-,omitempty"`
@@ -1032,6 +1033,13 @@ func (m *ConfigRequest_V1_Saml) GetNameIdPolicyFormat() *wrappers.StringValue {
 	return nil
 }
 
+func (m *ConfigRequest_V1_Saml) GetAllowedGroups() []string {
+	if m != nil {
+		return m.AllowedGroups
+	}
+	return nil
+}
+
 func init() {
 	proto.RegisterType((*ConfigRequest)(nil), "chef.automate.domain.dex.ConfigRequest")
 	proto.RegisterType((*ConfigRequest_V1)(nil), "chef.automate.domain.dex.ConfigRequest.V1")
@@ -1055,89 +1063,91 @@ func init() {
 }
 
 var fileDescriptor_3e591b6849cffa8a = []byte{
-	// 1341 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x98, 0xdd, 0x6e, 0x13, 0x47,
-	0x14, 0xc7, 0x95, 0xd8, 0xf1, 0xc7, 0x49, 0x42, 0xc2, 0x84, 0xc2, 0x6a, 0xa9, 0x10, 0x6a, 0xd5,
-	0x4f, 0x29, 0xeb, 0x26, 0x80, 0x54, 0x68, 0x69, 0x9b, 0x8f, 0x02, 0x09, 0xe1, 0x6b, 0x0d, 0xa8,
-	0xe2, 0x66, 0x35, 0xde, 0x1d, 0x6f, 0x56, 0x8c, 0x67, 0x36, 0x33, 0xe3, 0x10, 0xab, 0x52, 0xaf,
-	0xa0, 0x17, 0x7d, 0x8c, 0x3e, 0x43, 0xaf, 0x7a, 0xc9, 0x55, 0xef, 0xfa, 0x18, 0x7d, 0x00, 0x5e,
-	0xa0, 0x9a, 0x0f, 0x9b, 0x04, 0x07, 0xd8, 0x35, 0x5c, 0x72, 0x67, 0x67, 0xcf, 0xef, 0xef, 0x73,
-	0xe6, 0x9c, 0xf3, 0xdf, 0xcd, 0xc2, 0xa7, 0x38, 0xcf, 0x5a, 0x31, 0x67, 0xdd, 0x2c, 0x6d, 0x25,
-	0xe4, 0xc0, 0x7d, 0x8c, 0x04, 0xd9, 0xeb, 0x13, 0xa9, 0x82, 0x5c, 0x70, 0xc5, 0x91, 0x17, 0xef,
-	0x92, 0x6e, 0x80, 0xfb, 0x8a, 0xf7, 0xb0, 0x22, 0x41, 0xc2, 0x7b, 0x38, 0x63, 0x41, 0x42, 0x0e,
-	0xfc, 0x73, 0x87, 0x70, 0xb9, 0x8b, 0x05, 0x49, 0x5a, 0x29, 0xe5, 0x1d, 0x4c, 0x2d, 0xe9, 0x9f,
-	0x1d, 0xbf, 0xae, 0xa8, 0x74, 0x17, 0xb7, 0x63, 0xde, 0xcb, 0x39, 0x23, 0x4c, 0xc9, 0xd6, 0x50,
-	0x7c, 0x39, 0x15, 0x79, 0xdc, 0x32, 0xd7, 0xe3, 0xe5, 0x94, 0xb0, 0x65, 0xbc, 0xba, 0xec, 0x78,
-	0x2d, 0x85, 0x57, 0xf5, 0x97, 0x16, 0x66, 0x8c, 0x2b, 0xac, 0x32, 0xce, 0x86, 0x5a, 0xe7, 0x52,
-	0xce, 0x53, 0x4a, 0x2c, 0xd9, 0xe9, 0x77, 0x5b, 0x4f, 0x04, 0xce, 0x73, 0x22, 0xdc, 0xf5, 0x4f,
-	0xfe, 0xfc, 0x1c, 0xe6, 0x37, 0x8c, 0x4e, 0x68, 0x4b, 0x43, 0x57, 0x60, 0x7a, 0x7f, 0xc5, 0xab,
-	0x9c, 0x9f, 0xfa, 0x72, 0x76, 0xf5, 0xeb, 0xe0, 0x75, 0x15, 0x06, 0x47, 0xa0, 0xe0, 0xe1, 0x4a,
-	0x38, 0xbd, 0xbf, 0xe2, 0xff, 0xfd, 0x19, 0x4c, 0x3f, 0x5c, 0x41, 0x1b, 0x50, 0x91, 0x03, 0xe9,
-	0x4d, 0x19, 0x8d, 0x95, 0xe2, 0x1a, 0x41, 0x7b, 0x20, 0x15, 0xe9, 0x85, 0x9a, 0x46, 0x9b, 0x50,
-	0x91, 0xfb, 0xb1, 0x37, 0x6d, 0x44, 0x56, 0xcb, 0x88, 0x10, 0xb1, 0x9f, 0xc5, 0x24, 0xd4, 0xb8,
-	0xff, 0x5f, 0x0d, 0x6a, 0x56, 0x15, 0x5d, 0x84, 0x6a, 0x8f, 0x4a, 0xec, 0xd2, 0x3a, 0xff, 0x8a,
-	0x62, 0xc6, 0xba, 0x02, 0x07, 0xf6, 0x48, 0x83, 0x5b, 0x54, 0xe2, 0xd0, 0x44, 0xa3, 0x9f, 0xa0,
-	0x42, 0x79, 0xea, 0xd2, 0x08, 0x4a, 0xa4, 0xb1, 0xc3, 0xd3, 0x50, 0xa3, 0xa8, 0x0d, 0x75, 0x69,
-	0x53, 0x72, 0xa7, 0x7a, 0xb9, 0xf4, 0x89, 0x8c, 0x6a, 0x1a, 0x2a, 0xa1, 0x0d, 0xa8, 0xea, 0x89,
-	0xf0, 0xaa, 0x46, 0xb1, 0x55, 0x42, 0xf1, 0xba, 0xc8, 0xe3, 0xd0, 0xc0, 0x68, 0x07, 0xea, 0x52,
-	0x71, 0x81, 0x53, 0xe2, 0xcd, 0x94, 0x3f, 0x66, 0x4b, 0x86, 0x43, 0x09, 0xb4, 0x05, 0x35, 0x72,
-	0x90, 0x67, 0x62, 0xe0, 0xd5, 0x4a, 0x37, 0xfe, 0x67, 0x03, 0x86, 0x4e, 0x00, 0x85, 0xd0, 0xec,
-	0x70, 0xae, 0xa4, 0x12, 0x38, 0xf7, 0xea, 0x46, 0xed, 0x62, 0x09, 0xb5, 0xf5, 0x21, 0x1b, 0xbe,
-	0x94, 0x41, 0x0f, 0x00, 0x62, 0xce, 0x18, 0x89, 0x15, 0x17, 0xd2, 0x6b, 0x18, 0xd1, 0x4b, 0x25,
-	0x44, 0x37, 0x46, 0x70, 0x78, 0x48, 0x08, 0x7d, 0x0f, 0x15, 0x45, 0xa5, 0xd7, 0x3c, 0x76, 0x5f,
-	0x8e, 0x0c, 0xd5, 0xfd, 0x9d, 0xf6, 0x86, 0x20, 0x09, 0x61, 0x2a, 0xc3, 0x54, 0x86, 0x1a, 0xf3,
-	0xff, 0x99, 0x82, 0xba, 0xeb, 0x2d, 0xfa, 0x06, 0xaa, 0xbb, 0x5c, 0x2a, 0x37, 0x9f, 0x1f, 0x07,
-	0x76, 0x73, 0x83, 0xe1, 0xe6, 0x06, 0x6d, 0x25, 0x32, 0x96, 0x3e, 0xc4, 0xb4, 0x4f, 0x42, 0x13,
-	0x89, 0x6e, 0x40, 0x35, 0xe7, 0x42, 0xb9, 0xe1, 0x3c, 0x3b, 0x46, 0x6c, 0x31, 0x75, 0x61, 0xd5,
-	0x00, 0xeb, 0x67, 0x9e, 0xbf, 0xf0, 0x96, 0x46, 0x83, 0xb8, 0xf8, 0xf4, 0x8e, 0x3f, 0xb3, 0xab,
-	0x54, 0x2e, 0x43, 0xa3, 0x80, 0xd6, 0x60, 0x9e, 0x1c, 0x28, 0x22, 0x18, 0xa6, 0x51, 0x77, 0x2f,
-	0x61, 0x6e, 0x52, 0xdf, 0x9c, 0xc4, 0xdc, 0x10, 0xb9, 0xb6, 0x97, 0x30, 0xbf, 0x39, 0xaa, 0xc4,
-	0xbf, 0x0c, 0x95, 0x1d, 0x9e, 0xa2, 0x55, 0x98, 0xa1, 0x64, 0x9f, 0xd0, 0x42, 0x15, 0xd9, 0x50,
-	0xff, 0x57, 0xa8, 0xbb, 0xc1, 0x42, 0xdf, 0x42, 0x23, 0xc1, 0x0a, 0x77, 0xb0, 0x24, 0x85, 0x14,
-	0x46, 0xd1, 0xfa, 0x24, 0xfb, 0x92, 0x08, 0x77, 0x2e, 0x6f, 0x39, 0x49, 0x1d, 0xb9, 0x5d, 0x6d,
-	0x54, 0x16, 0x67, 0xfc, 0xdf, 0xa0, 0xaa, 0xb7, 0x63, 0x82, 0x4e, 0x6c, 0x16, 0xef, 0xc4, 0xd2,
-	0xf3, 0x17, 0xde, 0x82, 0xdd, 0xde, 0xc5, 0x67, 0x77, 0x7c, 0xf3, 0xc1, 0x76, 0xc1, 0x7f, 0x3a,
-	0x05, 0x35, 0xbb, 0x09, 0xe8, 0x47, 0x98, 0x93, 0x59, 0xca, 0x32, 0x96, 0x46, 0x8f, 0xc9, 0xc8,
-	0x4b, 0xdf, 0x9c, 0xca, 0xac, 0x23, 0x6e, 0x92, 0x81, 0x44, 0x97, 0xa1, 0x99, 0x25, 0x91, 0xe2,
-	0x8f, 0x09, 0x93, 0x85, 0x0e, 0xa2, 0x91, 0x25, 0xf7, 0x4d, 0xb4, 0x7f, 0x1b, 0x9a, 0xa3, 0x0d,
-	0x42, 0x6b, 0x70, 0x22, 0x63, 0x92, 0xc4, 0x7d, 0x41, 0x22, 0x9c, 0xf4, 0x32, 0xe6, 0x52, 0xf1,
-	0xc7, 0xc4, 0xd6, 0x39, 0xa7, 0x56, 0x6a, 0x7e, 0x48, 0xac, 0x69, 0xc0, 0xff, 0x6b, 0x1a, 0xe0,
-	0xe5, 0xf6, 0x68, 0x9f, 0xb0, 0xe8, 0x04, 0x37, 0x88, 0xeb, 0x06, 0x0c, 0x9d, 0x80, 0x76, 0x41,
-	0x9a, 0xe0, 0xdc, 0xd5, 0x57, 0xc6, 0x05, 0x77, 0x12, 0x9c, 0x87, 0x06, 0xd6, 0x22, 0x12, 0xf7,
-	0xa8, 0x1b, 0xf9, 0x32, 0x22, 0x6d, 0xdc, 0xa3, 0xa1, 0x81, 0xd1, 0x3d, 0x68, 0xf6, 0x24, 0x4e,
-	0x22, 0x93, 0x4e, 0xb5, 0xb4, 0x63, 0xdd, 0xd2, 0xac, 0xc9, 0xa9, 0xa1, 0x65, 0xf4, 0x27, 0xff,
-	0xf7, 0x29, 0xa8, 0xd9, 0x7a, 0x75, 0x33, 0x63, 0x9a, 0x11, 0xa6, 0xa2, 0x2c, 0x29, 0xb6, 0x0b,
-	0x36, 0x7c, 0x2b, 0xd1, 0x9b, 0xed, 0x50, 0x49, 0x62, 0x41, 0x54, 0xa1, 0x59, 0x98, 0xb3, 0x48,
-	0xdb, 0x10, 0xfe, 0xb3, 0x26, 0x54, 0x75, 0x46, 0xe8, 0x2a, 0xcc, 0xc6, 0x38, 0x8a, 0x39, 0x53,
-	0xfa, 0xd9, 0xa4, 0x50, 0x22, 0x10, 0xe3, 0x0d, 0x17, 0x3f, 0x5a, 0xab, 0xe9, 0xc2, 0x6b, 0x75,
-	0x09, 0xea, 0x9d, 0x8c, 0x25, 0x51, 0x41, 0x43, 0xaa, 0xe9, 0xe0, 0x4d, 0xa6, 0x6b, 0x36, 0x58,
-	0x8e, 0xa5, 0x7c, 0xc2, 0x45, 0xe2, 0x1a, 0xf2, 0x96, 0x9a, 0x35, 0x72, 0xd7, 0x11, 0xe8, 0x26,
-	0x2c, 0x69, 0x2b, 0x89, 0xb4, 0x3b, 0x44, 0x92, 0x60, 0x11, 0xef, 0xea, 0x2c, 0x66, 0x0a, 0x08,
-	0x2d, 0x6a, 0xf0, 0x81, 0x24, 0xa2, 0x6d, 0xb0, 0x4d, 0x86, 0x6e, 0xc0, 0x49, 0xa3, 0xb3, 0xd7,
-	0x27, 0x62, 0x10, 0x75, 0x33, 0xaa, 0x88, 0x70, 0x37, 0xc9, 0x37, 0x4b, 0x2d, 0x68, 0xec, 0x9e,
-	0xa6, 0xae, 0x19, 0x48, 0x57, 0xa6, 0xff, 0xc4, 0x70, 0x8f, 0x44, 0x58, 0x29, 0xe1, 0x6e, 0x8e,
-	0x6f, 0xa9, 0x6c, 0x88, 0xac, 0x29, 0x25, 0xd0, 0x0f, 0x60, 0xbe, 0x47, 0x59, 0x62, 0x15, 0x1a,
-	0x45, 0xba, 0xa8, 0x89, 0xad, 0xc4, 0xf0, 0xdf, 0x01, 0x90, 0x1e, 0xce, 0xa8, 0xa5, 0x9b, 0x05,
-	0xe8, 0xa6, 0x89, 0x37, 0xf0, 0x3d, 0x38, 0x6d, 0x7e, 0x3c, 0xc9, 0x64, 0x4e, 0xf1, 0x20, 0x7a,
-	0x59, 0x08, 0x14, 0x10, 0x5a, 0xd2, 0xec, 0xa6, 0x45, 0x6f, 0x0f, 0xeb, 0xb9, 0x05, 0xa7, 0x4c,
-	0xa7, 0x52, 0xc1, 0xfb, 0xf9, 0xa1, 0x56, 0xcd, 0x16, 0x10, 0x3c, 0xa9, 0xc9, 0xeb, 0x1a, 0x1c,
-	0xf5, 0x6a, 0x1b, 0x90, 0x55, 0x3a, 0xd2, 0xac, 0xb9, 0x22, 0x7d, 0x37, 0xdc, 0xe1, 0x6e, 0xfd,
-	0x02, 0xbe, 0xe5, 0x6d, 0x72, 0x32, 0xea, 0x0c, 0xec, 0x40, 0x99, 0x8a, 0xe7, 0x0b, 0x68, 0x9e,
-	0xb6, 0xbc, 0x49, 0x51, 0xae, 0x0f, 0xf4, 0x54, 0x99, 0xa2, 0x1f, 0xc1, 0xd9, 0xe3, 0x95, 0xf7,
-	0x35, 0xe6, 0x9d, 0x28, 0x20, 0x7d, 0x66, 0x5c, 0xda, 0x5c, 0x40, 0x6d, 0x38, 0x63, 0x4f, 0x60,
-	0xbc, 0x49, 0x0b, 0x05, 0x74, 0x4f, 0x19, 0xf8, 0xd5, 0x2e, 0xad, 0xc3, 0xc2, 0xe8, 0x36, 0xc2,
-	0x78, 0x24, 0x25, 0xf5, 0x16, 0x8b, 0xdf, 0x47, 0x6e, 0xf3, 0xb6, 0xa4, 0xfe, 0x1f, 0x4d, 0x68,
-	0x8e, 0x8c, 0xf2, 0x83, 0x19, 0x4d, 0x68, 0x46, 0xaf, 0xdb, 0x97, 0xda, 0x64, 0xfb, 0x72, 0xac,
-	0xb7, 0xd5, 0x27, 0xf1, 0xb6, 0xe3, 0x37, 0xaf, 0x31, 0xd1, 0xe6, 0x8d, 0xf9, 0x64, 0xf3, 0x9d,
-	0x7d, 0x12, 0xde, 0xc9, 0x27, 0x67, 0xdf, 0x97, 0x4f, 0xce, 0x4d, 0xea, 0x93, 0x1f, 0xcc, 0xe8,
-	0xbd, 0x9a, 0xd1, 0xbf, 0x15, 0xa8, 0xea, 0xe7, 0xbf, 0x77, 0xf5, 0xa1, 0x4b, 0x50, 0x97, 0x92,
-	0x47, 0x7d, 0x41, 0x0b, 0x59, 0x51, 0x4d, 0x4a, 0xfe, 0x40, 0xd0, 0x57, 0xa6, 0xab, 0x52, 0x6e,
-	0xba, 0xc6, 0xb6, 0xa3, 0x5a, 0x7a, 0x3b, 0xae, 0xc2, 0xac, 0x6b, 0xb6, 0x11, 0x28, 0x62, 0x45,
-	0x60, 0x81, 0x61, 0x06, 0xfa, 0xff, 0x60, 0x35, 0x88, 0x32, 0x29, 0xfb, 0x05, 0x9f, 0x86, 0xe6,
-	0x2c, 0xb2, 0x65, 0x08, 0x74, 0x07, 0x3e, 0x32, 0x05, 0x64, 0x49, 0x94, 0x73, 0x9a, 0xc5, 0x83,
-	0xa8, 0xcb, 0x45, 0x0f, 0xab, 0x42, 0xe6, 0x83, 0x34, 0xba, 0x95, 0xdc, 0x35, 0xe0, 0x35, 0xc3,
-	0x5d, 0x41, 0xcf, 0x5f, 0x78, 0x27, 0x60, 0x6e, 0xf4, 0xc6, 0x2d, 0x21, 0x07, 0xdb, 0xd5, 0xc6,
-	0xd4, 0x62, 0x65, 0xfd, 0xab, 0x47, 0x5f, 0xa4, 0x99, 0xda, 0xed, 0x77, 0x82, 0x98, 0xf7, 0x5a,
-	0xfa, 0xa9, 0x7e, 0xf4, 0x5e, 0xae, 0x75, 0xf4, 0x3d, 0x61, 0xa7, 0x66, 0x7e, 0xee, 0xc2, 0xff,
-	0x01, 0x00, 0x00, 0xff, 0xff, 0xdd, 0x68, 0xa0, 0xca, 0x40, 0x14, 0x00, 0x00,
+	// 1361 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x98, 0x5b, 0x6f, 0x13, 0x47,
+	0x14, 0xc7, 0x95, 0xd8, 0xf1, 0xe5, 0xc4, 0xb9, 0x30, 0xa1, 0xb0, 0x5a, 0x2a, 0x84, 0x5a, 0xd1,
+	0xd2, 0x4a, 0x59, 0x37, 0x01, 0xa4, 0x42, 0x4b, 0xdb, 0x5c, 0x0a, 0x24, 0x84, 0xdb, 0x1a, 0x50,
+	0xc5, 0xcb, 0x6a, 0xbc, 0x3b, 0xde, 0xac, 0x18, 0xcf, 0x6c, 0x66, 0xc6, 0x21, 0x56, 0xa5, 0x3e,
+	0x41, 0x1f, 0xfa, 0x79, 0xfa, 0x09, 0x78, 0x6a, 0x3f, 0x46, 0x9f, 0xfa, 0x01, 0x90, 0xfa, 0x5c,
+	0xcd, 0xc5, 0x26, 0xc1, 0x01, 0xd6, 0x86, 0x47, 0xde, 0x6c, 0xef, 0xf9, 0xfd, 0x73, 0xce, 0x9c,
+	0x73, 0xfe, 0xbb, 0x59, 0xf8, 0x1c, 0xe7, 0x59, 0x33, 0xe6, 0xac, 0x93, 0xa5, 0xcd, 0x84, 0x1c,
+	0xb8, 0x8f, 0x91, 0x20, 0x7b, 0x3d, 0x22, 0x55, 0x90, 0x0b, 0xae, 0x38, 0xf2, 0xe2, 0x5d, 0xd2,
+	0x09, 0x70, 0x4f, 0xf1, 0x2e, 0x56, 0x24, 0x48, 0x78, 0x17, 0x67, 0x2c, 0x48, 0xc8, 0x81, 0x7f,
+	0xf6, 0x10, 0x2e, 0x77, 0xb1, 0x20, 0x49, 0x33, 0xa5, 0xbc, 0x8d, 0xa9, 0x25, 0xfd, 0x33, 0xa3,
+	0xd7, 0x15, 0x95, 0xee, 0xe2, 0x76, 0xcc, 0xbb, 0x39, 0x67, 0x84, 0x29, 0xd9, 0x1c, 0x88, 0x2f,
+	0xa7, 0x22, 0x8f, 0x9b, 0xe6, 0x7a, 0xbc, 0x9c, 0x12, 0xb6, 0x8c, 0x57, 0x97, 0x1d, 0xaf, 0xa5,
+	0xf0, 0xaa, 0xfe, 0xd2, 0xc4, 0x8c, 0x71, 0x85, 0x55, 0xc6, 0xd9, 0x40, 0xeb, 0x6c, 0xca, 0x79,
+	0x4a, 0x89, 0x25, 0xdb, 0xbd, 0x4e, 0xf3, 0xa9, 0xc0, 0x79, 0x4e, 0x84, 0xbb, 0xfe, 0xd9, 0xdf,
+	0x5f, 0xc0, 0xdc, 0x86, 0xd1, 0x09, 0x6d, 0x69, 0xe8, 0x2a, 0x4c, 0xef, 0xaf, 0x78, 0xa5, 0x73,
+	0x53, 0x17, 0x66, 0x57, 0xbf, 0x0e, 0xde, 0x54, 0x61, 0x70, 0x04, 0x0a, 0x1e, 0xad, 0x84, 0xd3,
+	0xfb, 0x2b, 0xfe, 0x3f, 0xe7, 0x61, 0xfa, 0xd1, 0x0a, 0xda, 0x80, 0x92, 0xec, 0x4b, 0x6f, 0xca,
+	0x68, 0xac, 0x14, 0xd7, 0x08, 0x5a, 0x7d, 0xa9, 0x48, 0x37, 0xd4, 0x34, 0xda, 0x84, 0x92, 0xdc,
+	0x8f, 0xbd, 0x69, 0x23, 0xb2, 0x3a, 0x8e, 0x08, 0x11, 0xfb, 0x59, 0x4c, 0x42, 0x8d, 0xfb, 0xff,
+	0x56, 0xa0, 0x62, 0x55, 0xd1, 0x25, 0x28, 0x77, 0xa9, 0xc4, 0x2e, 0xad, 0x73, 0xaf, 0x29, 0x66,
+	0xac, 0x23, 0x70, 0x60, 0x8f, 0x34, 0xb8, 0x4d, 0x25, 0x0e, 0x4d, 0x34, 0xfa, 0x09, 0x4a, 0x94,
+	0xa7, 0x2e, 0x8d, 0x60, 0x8c, 0x34, 0x76, 0x78, 0x1a, 0x6a, 0x14, 0xb5, 0xa0, 0x2a, 0x6d, 0x4a,
+	0xee, 0x54, 0xaf, 0x8c, 0x7d, 0x22, 0xc3, 0x9a, 0x06, 0x4a, 0x68, 0x03, 0xca, 0x7a, 0x22, 0xbc,
+	0xb2, 0x51, 0x6c, 0x8e, 0xa1, 0x78, 0x43, 0xe4, 0x71, 0x68, 0x60, 0xb4, 0x03, 0x55, 0xa9, 0xb8,
+	0xc0, 0x29, 0xf1, 0x66, 0xc6, 0x3f, 0x66, 0x4b, 0x86, 0x03, 0x09, 0xb4, 0x05, 0x15, 0x72, 0x90,
+	0x67, 0xa2, 0xef, 0x55, 0xc6, 0x6e, 0xfc, 0xcf, 0x06, 0x0c, 0x9d, 0x00, 0x0a, 0xa1, 0xde, 0xe6,
+	0x5c, 0x49, 0x25, 0x70, 0xee, 0x55, 0x8d, 0xda, 0xa5, 0x31, 0xd4, 0xd6, 0x07, 0x6c, 0xf8, 0x4a,
+	0x06, 0x3d, 0x04, 0x88, 0x39, 0x63, 0x24, 0x56, 0x5c, 0x48, 0xaf, 0x66, 0x44, 0x2f, 0x8f, 0x21,
+	0xba, 0x31, 0x84, 0xc3, 0x43, 0x42, 0xe8, 0x7b, 0x28, 0x29, 0x2a, 0xbd, 0xfa, 0xb1, 0xfb, 0x72,
+	0x64, 0xa8, 0x1e, 0xec, 0xb4, 0x36, 0x04, 0x49, 0x08, 0x53, 0x19, 0xa6, 0x32, 0xd4, 0x98, 0xff,
+	0xd7, 0x14, 0x54, 0x5d, 0x6f, 0xd1, 0x37, 0x50, 0xde, 0xe5, 0x52, 0xb9, 0xf9, 0xfc, 0x34, 0xb0,
+	0x9b, 0x1b, 0x0c, 0x36, 0x37, 0x68, 0x29, 0x91, 0xb1, 0xf4, 0x11, 0xa6, 0x3d, 0x12, 0x9a, 0x48,
+	0x74, 0x13, 0xca, 0x39, 0x17, 0xca, 0x0d, 0xe7, 0x99, 0x11, 0x62, 0x8b, 0xa9, 0x8b, 0xab, 0x06,
+	0x58, 0x3f, 0xfd, 0xe2, 0xa5, 0xb7, 0x34, 0x1c, 0xc4, 0xc5, 0x67, 0x77, 0xfd, 0x99, 0x5d, 0xa5,
+	0x72, 0x19, 0x1a, 0x05, 0xb4, 0x06, 0x73, 0xe4, 0x40, 0x11, 0xc1, 0x30, 0x8d, 0x3a, 0x7b, 0x09,
+	0x73, 0x93, 0xfa, 0xf6, 0x24, 0x1a, 0x03, 0xe4, 0xfa, 0x5e, 0xc2, 0xfc, 0xfa, 0xb0, 0x12, 0xff,
+	0x0a, 0x94, 0x76, 0x78, 0x8a, 0x56, 0x61, 0x86, 0x92, 0x7d, 0x42, 0x0b, 0x55, 0x64, 0x43, 0xfd,
+	0x5f, 0xa1, 0xea, 0x06, 0x0b, 0x7d, 0x0b, 0xb5, 0x04, 0x2b, 0xdc, 0xc6, 0x92, 0x14, 0x52, 0x18,
+	0x46, 0xeb, 0x93, 0xec, 0x49, 0x22, 0xdc, 0xb9, 0xbc, 0xe3, 0x24, 0x75, 0xe4, 0x76, 0xb9, 0x56,
+	0x5a, 0x9c, 0xf1, 0x7f, 0x83, 0xb2, 0xde, 0x8e, 0x09, 0x3a, 0xb1, 0x59, 0xbc, 0x13, 0x4b, 0x2f,
+	0x5e, 0x7a, 0x0b, 0x76, 0x7b, 0x17, 0x9f, 0xdf, 0xf5, 0xcd, 0x07, 0xdb, 0x05, 0xff, 0xd9, 0x14,
+	0x54, 0xec, 0x26, 0xa0, 0x1f, 0xa1, 0x21, 0xb3, 0x94, 0x65, 0x2c, 0x8d, 0x9e, 0x90, 0xa1, 0x97,
+	0xbe, 0x3d, 0x95, 0x59, 0x47, 0xdc, 0x22, 0x7d, 0x89, 0xae, 0x40, 0x3d, 0x4b, 0x22, 0xc5, 0x9f,
+	0x10, 0x26, 0x0b, 0x1d, 0x44, 0x2d, 0x4b, 0x1e, 0x98, 0x68, 0xff, 0x0e, 0xd4, 0x87, 0x1b, 0x84,
+	0xd6, 0x60, 0x3e, 0x63, 0x92, 0xc4, 0x3d, 0x41, 0x22, 0x9c, 0x74, 0x33, 0xe6, 0x52, 0xf1, 0x47,
+	0xc4, 0xd6, 0x39, 0xa7, 0x56, 0x6a, 0x6e, 0x40, 0xac, 0x69, 0xc0, 0xff, 0x73, 0x1a, 0xe0, 0xd5,
+	0xf6, 0x68, 0x9f, 0xb0, 0xe8, 0x04, 0x37, 0x88, 0x1b, 0x06, 0x0c, 0x9d, 0x80, 0x76, 0x41, 0x9a,
+	0xe0, 0xdc, 0xd5, 0x37, 0x8e, 0x0b, 0xee, 0x24, 0x38, 0x0f, 0x0d, 0xac, 0x45, 0x24, 0xee, 0x52,
+	0x37, 0xf2, 0xe3, 0x88, 0xb4, 0x70, 0x97, 0x86, 0x06, 0x46, 0xf7, 0xa1, 0xde, 0x95, 0x38, 0x89,
+	0x4c, 0x3a, 0xe5, 0xb1, 0x1d, 0xeb, 0xb6, 0x66, 0x4d, 0x4e, 0x35, 0x2d, 0xa3, 0x3f, 0xf9, 0xbf,
+	0x4f, 0x41, 0xc5, 0xd6, 0xab, 0x9b, 0x19, 0xd3, 0x8c, 0x30, 0x15, 0x65, 0x49, 0xb1, 0x5d, 0xb0,
+	0xe1, 0x5b, 0x89, 0xde, 0x6c, 0x87, 0x4a, 0x12, 0x0b, 0xa2, 0x0a, 0xcd, 0x42, 0xc3, 0x22, 0x2d,
+	0x43, 0xf8, 0xcf, 0xeb, 0x50, 0xd6, 0x19, 0xa1, 0x6b, 0x30, 0x1b, 0xe3, 0x28, 0xe6, 0x4c, 0xe9,
+	0x67, 0x93, 0x42, 0x89, 0x40, 0x8c, 0x37, 0x5c, 0xfc, 0x70, 0xad, 0xa6, 0x0b, 0xaf, 0xd5, 0x65,
+	0xa8, 0xb6, 0x33, 0x96, 0x44, 0x05, 0x0d, 0xa9, 0xa2, 0x83, 0x37, 0x99, 0xae, 0xd9, 0x60, 0x39,
+	0x96, 0xf2, 0x29, 0x17, 0x89, 0x6b, 0xc8, 0x3b, 0x6a, 0xd6, 0xc8, 0x3d, 0x47, 0xa0, 0x5b, 0xb0,
+	0xa4, 0xad, 0x24, 0xd2, 0xee, 0x10, 0x49, 0x82, 0x45, 0xbc, 0xab, 0xb3, 0x98, 0x29, 0x20, 0xb4,
+	0xa8, 0xc1, 0x87, 0x92, 0x88, 0x96, 0xc1, 0x36, 0x19, 0xba, 0x09, 0x27, 0x8c, 0xce, 0x5e, 0x8f,
+	0x88, 0x7e, 0xd4, 0xc9, 0xa8, 0x22, 0xc2, 0xdd, 0x24, 0xdf, 0x2e, 0xb5, 0xa0, 0xb1, 0xfb, 0x9a,
+	0xba, 0x6e, 0x20, 0x5d, 0x99, 0xfe, 0x89, 0xe1, 0x2e, 0x89, 0xb0, 0x52, 0xc2, 0xdd, 0x1c, 0xdf,
+	0x51, 0xd9, 0x00, 0x59, 0x53, 0x4a, 0xa0, 0x1f, 0xc0, 0x7c, 0x8f, 0xb2, 0xc4, 0x2a, 0xd4, 0x8a,
+	0x74, 0x51, 0x13, 0x5b, 0x89, 0xe1, 0xbf, 0x03, 0x20, 0x5d, 0x9c, 0x51, 0x4b, 0xd7, 0x0b, 0xd0,
+	0x75, 0x13, 0x6f, 0xe0, 0xfb, 0x70, 0xca, 0xfc, 0xf1, 0x24, 0x93, 0x39, 0xc5, 0xfd, 0xe8, 0x55,
+	0x21, 0x50, 0x40, 0x68, 0x49, 0xb3, 0x9b, 0x16, 0xbd, 0x33, 0xa8, 0xe7, 0x36, 0x9c, 0x34, 0x9d,
+	0x4a, 0x05, 0xef, 0xe5, 0x87, 0x5a, 0x35, 0x5b, 0x40, 0xf0, 0x84, 0x26, 0x6f, 0x68, 0x70, 0xd8,
+	0xab, 0x6d, 0x40, 0x56, 0xe9, 0x48, 0xb3, 0x1a, 0x45, 0xfa, 0x6e, 0xb8, 0xc3, 0xdd, 0xfa, 0x05,
+	0x7c, 0xcb, 0xdb, 0xe4, 0x64, 0xd4, 0xee, 0xdb, 0x81, 0x32, 0x15, 0xcf, 0x15, 0xd0, 0x3c, 0x65,
+	0x79, 0x93, 0xa2, 0x5c, 0xef, 0xeb, 0xa9, 0x32, 0x45, 0x3f, 0x86, 0x33, 0xc7, 0x2b, 0xef, 0x6b,
+	0xcc, 0x9b, 0x2f, 0x20, 0x7d, 0x7a, 0x54, 0xda, 0x5c, 0x40, 0x2d, 0x38, 0x6d, 0x4f, 0x60, 0xb4,
+	0x49, 0x0b, 0x05, 0x74, 0x4f, 0x1a, 0xf8, 0xf5, 0x2e, 0xad, 0xc3, 0xc2, 0xf0, 0x36, 0xc2, 0x78,
+	0x24, 0x25, 0xf5, 0x16, 0x8b, 0xdf, 0x47, 0xee, 0xf0, 0x96, 0xa4, 0xfe, 0x1f, 0x75, 0xa8, 0x0f,
+	0x8d, 0xf2, 0xa3, 0x19, 0x4d, 0x68, 0x46, 0x6f, 0xda, 0x97, 0xca, 0x64, 0xfb, 0x72, 0xac, 0xb7,
+	0x55, 0x27, 0xf1, 0xb6, 0xe3, 0x37, 0xaf, 0x36, 0xd1, 0xe6, 0x8d, 0xf8, 0x64, 0xfd, 0xbd, 0x7d,
+	0x12, 0xde, 0xcb, 0x27, 0x67, 0x3f, 0x94, 0x4f, 0x36, 0x26, 0xf5, 0xc9, 0x8f, 0x66, 0xf4, 0x41,
+	0xcd, 0xe8, 0xbf, 0x12, 0x94, 0xf5, 0xf3, 0xdf, 0xfb, 0xfa, 0xd0, 0x65, 0xa8, 0x4a, 0xc9, 0xa3,
+	0x9e, 0xa0, 0x85, 0xac, 0xa8, 0x22, 0x25, 0x7f, 0x28, 0xe8, 0x6b, 0xd3, 0x55, 0x1a, 0x6f, 0xba,
+	0x46, 0xb6, 0xa3, 0x3c, 0xf6, 0x76, 0x5c, 0x83, 0x59, 0xd7, 0x6c, 0x23, 0x50, 0xc4, 0x8a, 0xc0,
+	0x02, 0x83, 0x0c, 0xf4, 0xff, 0xc1, 0xaa, 0x1f, 0x65, 0x52, 0xf6, 0x0a, 0x3e, 0x0d, 0x35, 0x2c,
+	0xb2, 0x65, 0x08, 0x74, 0x17, 0x3e, 0x31, 0x05, 0x64, 0x49, 0x94, 0x73, 0x9a, 0xc5, 0xfd, 0xa8,
+	0xc3, 0x45, 0x17, 0xab, 0x42, 0xe6, 0x83, 0x34, 0xba, 0x95, 0xdc, 0x33, 0xe0, 0x75, 0xc3, 0xa1,
+	0xf3, 0x30, 0x8f, 0x29, 0xe5, 0x4f, 0x49, 0xe2, 0xe6, 0xd8, 0xab, 0x9d, 0x2b, 0x5d, 0xa8, 0x87,
+	0x73, 0xee, 0x57, 0x3b, 0x9c, 0x57, 0xd1, 0x8b, 0x97, 0xde, 0x3c, 0x34, 0x86, 0x2f, 0xe6, 0x12,
+	0x72, 0xb0, 0x5d, 0xae, 0x4d, 0x2d, 0x96, 0xd6, 0xbf, 0x7a, 0xfc, 0x65, 0x9a, 0xa9, 0xdd, 0x5e,
+	0x3b, 0x88, 0x79, 0xb7, 0xa9, 0x1f, 0xfe, 0x87, 0xaf, 0xef, 0x9a, 0x47, 0x5f, 0x27, 0xb6, 0x2b,
+	0x26, 0xab, 0x8b, 0xff, 0x07, 0x00, 0x00, 0xff, 0xff, 0x94, 0x03, 0x11, 0xbc, 0x67, 0x14, 0x00,
+	0x00,
 }
diff --git a/api/config/dex/config_request.proto b/api/config/dex/config_request.proto
index 70132badb..511381445 100644
--- a/api/config/dex/config_request.proto
+++ b/api/config/dex/config_request.proto
@@ -123,6 +123,7 @@ message ConfigRequest {
 			google.protobuf.StringValue groups_attr = 5;
 			google.protobuf.StringValue entity_issuer = 6;
 			google.protobuf.StringValue name_id_policy_format = 7;
+			repeated string allowed_groups = 8;
 		}
 	}
 }
diff --git a/components/automate-chef-io/content/docs/saml.md b/components/automate-chef-io/content/docs/saml.md
index 1723b754c..4da52744b 100644
--- a/components/automate-chef-io/content/docs/saml.md
+++ b/components/automate-chef-io/content/docs/saml.md
@@ -59,6 +59,7 @@ sso_url = "<your SSO URL>"                  # required
 email_attr = "<your email attribute>"       # required
 username_attr = "<your username attribute>" # required
 groups_attr = "<your groups attribute>"     # optional
+allowed_groups = ["group1", "group 2"]      # optional
 entity_issuer = "<your entity issuer>"      # optional
 name_id_policy_format = "<see below>"       # optional
 ```
@@ -78,10 +79,13 @@ k2WFcoiiKyeIznNScx/K6AeykKR/lPrJedanSA==
 ```
 
 {{% warning %}}
-The `groups_attr` setting is optional. However, if it is not provided,
-users authenticating via SAML will not be members of any teams.
+The `groups_attr` setting is optional, but if not provided, users authenticating via SAML will not be members of any teams.
 {{% /warning %}}
 
+Setting `allowed_groups` provides SAML sign in to members of the listed groups.
+All of the other user groups that are _not_ in the list are discarded, and not available to Chef Automate.
+In the configuration example above, only users in either "group1" or "group2" may sign in, and those groups would appear as `team:saml:group1` and `team:saml:group2` respectively.
+
 Chef Automate supports using SAML to authenticate users and apply permissions to SAML groups. See [IAM Overview]({{< relref "iam-v2-overview.md" >}}).
 
 ```toml
diff --git a/components/automate-dex/habitat/plan.sh b/components/automate-dex/habitat/plan.sh
index f7711806d..d3a9f493f 100644
--- a/components/automate-dex/habitat/plan.sh
+++ b/components/automate-dex/habitat/plan.sh
@@ -16,7 +16,7 @@ pkg_binds=(
   [pg-sidecar-service]="port"
 )
 pkg_deps=(
-  core/dex/2.19.0
+  core/dex/2.24.0
   chef/mlsa
   ${local_platform_tools_origin:-chef}/automate-platform-tools
   core/bash
diff --git a/components/automate-dex/habitat/templates/config.yml b/components/automate-dex/habitat/templates/config.yml
index f29edd636..0ff68aa37 100644
--- a/components/automate-dex/habitat/templates/config.yml
+++ b/components/automate-dex/habitat/templates/config.yml
@@ -211,6 +211,13 @@ connectors:
       usernameAttr: {{cfg.connectors.saml.username_attr}}
       emailAttr: {{cfg.connectors.saml.email_attr}}
       groupsAttr: {{cfg.connectors.saml.groups_attr}}
+      {{- if cfg.connectors.saml.allowed_groups}}
+      allowedGroups:
+        {{- range cfg.connectors.saml.allowed_groups}}
+        - {{.}}
+        {{- end}}
+      filterGroups: true
+      {{- end}}
       nameIDPolicyFormat: {{cfg.connectors.saml.name_id_policy_format}}
   {{- end}}
 {{- end}}
diff --git a/components/automate-dex/web/templates/login.html b/components/automate-dex/web/templates/login.html
index e81e70566..139d5d4b3 100644
--- a/components/automate-dex/web/templates/login.html
+++ b/components/automate-dex/web/templates/login.html
@@ -7,12 +7,12 @@
     <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
     <title>{{ issuer }}</title>
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <link href="{{ url "static/main.css" }}" rel="stylesheet">
-    <link href="{{ url "theme/styles.css" }}" rel="stylesheet">
+    <link href="{{ url .ReqPath "static/main.css" }}" rel="stylesheet">
+    <link href="{{ url .ReqPath "theme/styles.css" }}" rel="stylesheet">
   </head>
   <div class="theme-panel">
     <div class="logo">
-      <img class="theme-navbar__logo" src="{{url "static/img/automate-blue-d9789f4b.svg" }}">
+      <img class="theme-navbar__logo" src="{{url .ReqPath "static/img/automate-blue-d9789f4b.svg" }}">
     </div>
     <div>
       {{ range $c := .Connectors }}
diff --git a/components/automate-dex/web/templates/password.html b/components/automate-dex/web/templates/password.html
index dccae35fe..5424dad85 100644
--- a/components/automate-dex/web/templates/password.html
+++ b/components/automate-dex/web/templates/password.html
@@ -7,9 +7,9 @@
     <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
     <title>{{ issuer }}</title>
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <link href="{{ url "static/main.css" }}" rel="stylesheet">
-    <link href="{{ url "theme/styles.css" }}" rel="stylesheet">
-    <link rel="stylesheet" href="{{ url "static/font-awesome-4.7.0/css/font-awesome.min.css" }}">
+    <link href="{{ url .ReqPath "static/main.css" }}" rel="stylesheet">
+    <link href="{{ url .ReqPath "theme/styles.css" }}" rel="stylesheet">
+    <link rel="stylesheet" href="{{ url .ReqPath "static/font-awesome-4.7.0/css/font-awesome.min.css" }}">
   </head>
   {{ if .BackLink }}
   <a class="back" href="javascript:history.back()">
@@ -21,7 +21,7 @@
   {{ end }}
   <div class="theme-panel">
     <div class="logo">
-      <img class="theme-navbar__logo" src="{{url "static/img/automate-blue-d9789f4b.svg" }}">
+      <img class="theme-navbar__logo" src="{{url .ReqPath "static/img/automate-blue-d9789f4b.svg" }}">
     </div>
     {{ if .Invalid }}
     <div class="dex-error-box">
diff --git a/components/automate-ui/src/app/app.module.ts b/components/automate-ui/src/app/app.module.ts
index b2075cca1..0e432977f 100644
--- a/components/automate-ui/src/app/app.module.ts
+++ b/components/automate-ui/src/app/app.module.ts
@@ -71,6 +71,7 @@ import { CookbookDetailsRequests } from './entities/cookbooks/cookbook-details.r
 import { CookbookVersionsRequests } from './entities/cookbooks/cookbook-versions.requests';
 import { ClientRunsRequests } from './entities/client-runs/client-runs.requests';
 import { CredentialRequests } from './entities/credentials/credential.requests';
+import { DataBagsRequests } from './entities/data-bags/data-bags.requests';
 import { DesktopRequests } from './entities/desktop/desktop.requests';
 import { DestinationRequests } from './entities/destinations/destination.requests';
 import { EnvironmentRequests } from './entities/environments/environment.requests';
@@ -292,6 +293,7 @@ import { WelcomeModalComponent } from './page-components/welcome-modal/welcome-m
     CookbookRequests,
     CookbookVersionsRequests,
     CredentialRequests,
+    DataBagsRequests,
     DesktopRequests,
     DestinationRequests,
     EnvironmentRequests,
diff --git a/components/automate-ui/src/app/components/sidebar-entry/sidebar-entry.component.html b/components/automate-ui/src/app/components/sidebar-entry/sidebar-entry.component.html
index 33f0ab155..f44b65d3d 100644
--- a/components/automate-ui/src/app/components/sidebar-entry/sidebar-entry.component.html
+++ b/components/automate-ui/src/app/components/sidebar-entry/sidebar-entry.component.html
@@ -12,6 +12,7 @@
 <ng-template #sidebarEntryTemplate>
   <chef-icon class="sidebar-icon" *ngIf="icon"
     [style.transform]="'rotate(' + (iconRotation ? iconRotation : 0) + 'deg)'">{{icon}}</chef-icon>
+  <div *ngIf="customIcon" [attr.class]="customIcon"></div>   
   <span class="text"><ng-content></ng-content></span>
   <chef-icon *ngIf="!openInNewPage" class="arrow">play_arrow</chef-icon>
   <chef-icon *ngIf="openInNewPage" class="open-in-new sidebar-icon">launch</chef-icon>
diff --git a/components/automate-ui/src/app/components/sidebar-entry/sidebar-entry.component.ts b/components/automate-ui/src/app/components/sidebar-entry/sidebar-entry.component.ts
index 39436cdeb..ee456dd04 100644
--- a/components/automate-ui/src/app/components/sidebar-entry/sidebar-entry.component.ts
+++ b/components/automate-ui/src/app/components/sidebar-entry/sidebar-entry.component.ts
@@ -6,7 +6,6 @@ import { Component, Input } from '@angular/core';
   styleUrls: ['./sidebar-entry.component.scss']
 })
 export class SidebarEntryComponent {
-  @Input() icon: string;
   @Input() route: string;
   @Input() exact: string;
   // (optional) 0-365 number to rotate icon via CSS.
@@ -14,6 +13,13 @@ export class SidebarEntryComponent {
   // (optional) if true, opens the link in a new page
   @Input() openInNewPage: boolean;
 
+  // (optional) both icon and custom icon are optional.
+  // icon is use to show material icon provided in service
+  // customeIcon is use to show customised icon.
+  // In custom icon we have to pass class name as a value to property from layout-sidebar service.
+  @Input() icon: string;
+  @Input() customIcon: string;
+
   isExactPresent() {
     // If the exact attribute is present, its value is the empty string.
     // If it is absent, it is undefined.
diff --git a/components/automate-ui/src/app/components/sidebar/sidebar.component.html b/components/automate-ui/src/app/components/sidebar/sidebar.component.html
index dba2c3b69..65677b4d7 100644
--- a/components/automate-ui/src/app/components/sidebar/sidebar.component.html
+++ b/components/automate-ui/src/app/components/sidebar/sidebar.component.html
@@ -15,6 +15,7 @@
                 [id]="'entry' + groupIndex + '-' + itemIndex"
                 [route]="menuItem.route"
                 [icon]="menuItem.icon"
+                [customIcon]="menuItem.customIcon"
                 [openInNewPage]="menuItem.openInNewPage"
               >{{ menuItem.name }}</chef-sidebar-entry>
               <chef-tooltip
@@ -31,6 +32,7 @@
                   [id]="'entry' + groupIndex + '-' + itemIndex"
                   [route]="menuItem.route"
                   [icon]="menuItem.icon"
+                  [customIcon]="menuItem.customIcon"
                   [openInNewPage]="menuItem.openInNewPage"
                 >{{ menuItem.name }}</chef-sidebar-entry>
                 <chef-tooltip
diff --git a/components/automate-ui/src/app/components/sidebar/sidebar.component.scss b/components/automate-ui/src/app/components/sidebar/sidebar.component.scss
index 2fad84dc3..1852bcf9b 100644
--- a/components/automate-ui/src/app/components/sidebar/sidebar.component.scss
+++ b/components/automate-ui/src/app/components/sidebar/sidebar.component.scss
@@ -105,6 +105,15 @@
             margin-right: -3px;
           }
         }
+
+        &:hover .chef-servers-icon, &.active .chef-servers-icon {
+          border-color: $chef-primary-bright;
+        }
+
+        &:hover .chef-servers-icon:before, &.active .chef-servers-icon:before {
+          color: $chef-primary-bright;
+        }
+
       }
 
       .sidebar-icon {
diff --git a/components/automate-ui/src/app/entities/data-bags/data-bags.action.ts b/components/automate-ui/src/app/entities/data-bags/data-bags.action.ts
new file mode 100644
index 000000000..402f1feb2
--- /dev/null
+++ b/components/automate-ui/src/app/entities/data-bags/data-bags.action.ts
@@ -0,0 +1,33 @@
+import { HttpErrorResponse } from '@angular/common/http';
+import { Action } from '@ngrx/store';
+import { DataBags } from './data-bags.model';
+
+export enum DataBagsActionTypes {
+  GET_ALL = 'DATA_BAGS::GET_ALL',
+  GET_ALL_SUCCESS = 'DATA_BAGS::GET_ALL::SUCCESS',
+  GET_ALL_FAILURE = 'DATA_BAGS::GET_ALL::FAILURE'
+}
+
+export interface DataBagsSuccessPayload {
+  data_bags: DataBags[];
+}
+
+export class GetDataBags implements Action {
+  readonly type = DataBagsActionTypes.GET_ALL;
+  constructor(public payload: { server_id: string, org_id: string }) { }
+}
+
+export class GetDataBagsSuccess implements Action {
+  readonly type = DataBagsActionTypes.GET_ALL_SUCCESS;
+  constructor(public payload: DataBagsSuccessPayload) { }
+}
+
+export class GetDataBagsFailure implements Action {
+  readonly type = DataBagsActionTypes.GET_ALL_FAILURE;
+  constructor(public payload: HttpErrorResponse) { }
+}
+
+export type DataBagsActions =
+  | GetDataBags
+  | GetDataBagsSuccess
+  | GetDataBagsFailure;
diff --git a/components/automate-ui/src/app/entities/data-bags/data-bags.effects.ts b/components/automate-ui/src/app/entities/data-bags/data-bags.effects.ts
new file mode 100644
index 000000000..8421aeef6
--- /dev/null
+++ b/components/automate-ui/src/app/entities/data-bags/data-bags.effects.ts
@@ -0,0 +1,45 @@
+import { Injectable } from '@angular/core';
+import { HttpErrorResponse } from '@angular/common/http';
+import { Actions, Effect, ofType } from '@ngrx/effects';
+import { of as observableOf } from 'rxjs';
+import { catchError, mergeMap, map } from 'rxjs/operators';
+import { CreateNotification } from 'app/entities/notifications/notification.actions';
+import { Type } from 'app/entities/notifications/notification.model';
+
+import {
+  GetDataBags,
+  GetDataBagsSuccess,
+  GetDataBagsFailure,
+  DataBagsSuccessPayload,
+  DataBagsActionTypes
+} from './data-bags.action';
+
+import { DataBagsRequests } from './data-bags.requests';
+
+@Injectable()
+export class DataBagsEffects {
+  constructor(
+    private actions$: Actions,
+    private requests: DataBagsRequests
+  ) { }
+
+  @Effect()
+  getDataBags$ = this.actions$.pipe(
+      ofType(DataBagsActionTypes.GET_ALL),
+      mergeMap(({ payload: { server_id, org_id } }: GetDataBags) =>
+        this.requests.getDataBags(server_id, org_id).pipe(
+          map((resp: DataBagsSuccessPayload) => new GetDataBagsSuccess(resp)),
+          catchError((error: HttpErrorResponse) => observableOf(new GetDataBagsFailure(error))))));
+
+  @Effect()
+  getDataBagsFailure$ = this.actions$.pipe(
+      ofType(DataBagsActionTypes.GET_ALL_FAILURE),
+      map(({ payload }: GetDataBagsFailure) => {
+        const msg = payload.error.error;
+        return new CreateNotification({
+          type: Type.error,
+          message: `Could not get infra data bags: ${msg || payload.error}`
+        });
+      }));
+
+}
diff --git a/components/automate-ui/src/app/entities/data-bags/data-bags.model.ts b/components/automate-ui/src/app/entities/data-bags/data-bags.model.ts
new file mode 100644
index 000000000..af5983b5a
--- /dev/null
+++ b/components/automate-ui/src/app/entities/data-bags/data-bags.model.ts
@@ -0,0 +1,4 @@
+export interface DataBags {
+  name: string;
+}
+
diff --git a/components/automate-ui/src/app/entities/data-bags/data-bags.reducer.ts b/components/automate-ui/src/app/entities/data-bags/data-bags.reducer.ts
new file mode 100644
index 000000000..ce6b1264e
--- /dev/null
+++ b/components/automate-ui/src/app/entities/data-bags/data-bags.reducer.ts
@@ -0,0 +1,44 @@
+import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity';
+import { set, pipe } from 'lodash/fp';
+import { EntityStatus } from 'app/entities/entities';
+import { DataBagsActionTypes, DataBagsActions } from './data-bags.action';
+import { DataBags } from './data-bags.model';
+
+export interface DataBagsEntityState extends EntityState<DataBags> {
+  getAllStatus: EntityStatus;
+}
+
+const GET_ALL_STATUS = 'getAllStatus';
+
+export const dataBagsEntityAdapter: EntityAdapter<DataBags> = createEntityAdapter<DataBags>({
+  selectId: (dataBags: DataBags) => dataBags.name
+});
+
+export const DataBagsEntityInitialState: DataBagsEntityState =
+dataBagsEntityAdapter.getInitialState(<DataBagsEntityState>{
+  getAllStatus: EntityStatus.notLoaded
+});
+
+export function dataBagsEntityReducer(
+  state: DataBagsEntityState = DataBagsEntityInitialState,
+  action: DataBagsActions): DataBagsEntityState {
+
+  switch (action.type) {
+    case DataBagsActionTypes.GET_ALL:
+      return set(GET_ALL_STATUS, EntityStatus.loading, dataBagsEntityAdapter.removeAll(state));
+
+    case DataBagsActionTypes.GET_ALL_SUCCESS:
+      return pipe(
+        set(GET_ALL_STATUS, EntityStatus.loadingSuccess))
+        (dataBagsEntityAdapter.setAll(action.payload.data_bags, state)) as DataBagsEntityState;
+
+    case DataBagsActionTypes.GET_ALL_FAILURE:
+      return set(GET_ALL_STATUS, EntityStatus.loadingFailure, state);
+
+    default:
+      return state;
+  }
+}
+
+export const getEntityById = (id: string) =>
+  (state: DataBagsEntityState) => state.entities[id];
diff --git a/components/automate-ui/src/app/entities/data-bags/data-bags.requests.ts b/components/automate-ui/src/app/entities/data-bags/data-bags.requests.ts
new file mode 100644
index 000000000..dd3a79d73
--- /dev/null
+++ b/components/automate-ui/src/app/entities/data-bags/data-bags.requests.ts
@@ -0,0 +1,16 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Observable } from 'rxjs';
+import { environment as env } from 'environments/environment';
+import { DataBagsSuccessPayload } from './data-bags.action';
+
+@Injectable()
+export class DataBagsRequests {
+
+  constructor(private http: HttpClient) { }
+
+  public getDataBags(server_id: string, org_id: string): Observable<DataBagsSuccessPayload> {
+    return this.http.get<DataBagsSuccessPayload>(
+      `${env.infra_proxy_url}/servers/${server_id}/orgs/${org_id}/data_bags`);
+  }
+}
diff --git a/components/automate-ui/src/app/entities/data-bags/data-bags.selectors.ts b/components/automate-ui/src/app/entities/data-bags/data-bags.selectors.ts
new file mode 100644
index 000000000..67508913d
--- /dev/null
+++ b/components/automate-ui/src/app/entities/data-bags/data-bags.selectors.ts
@@ -0,0 +1,21 @@
+import { createSelector, createFeatureSelector } from '@ngrx/store';
+import { DataBagsEntityState, dataBagsEntityAdapter } from './data-bags.reducer';
+import { routeParams } from 'app/route.selectors';
+import { find } from 'lodash/fp';
+
+export const dataBagsState = createFeatureSelector<DataBagsEntityState>('dataBags');
+export const {
+  selectAll: allDataBags,
+  selectEntities: dataBagsEntities
+} = dataBagsEntityAdapter.getSelectors(dataBagsState);
+
+export const getAllStatus = createSelector(
+  dataBagsState,
+  (state) => state.getAllStatus
+);
+
+export const dataBagsFromRoute = createSelector(
+  dataBagsState,
+  routeParams,
+  (state, { name }) => find({ name }, state)
+);
diff --git a/components/automate-ui/src/app/entities/layout/layout-sidebar.service.ts b/components/automate-ui/src/app/entities/layout/layout-sidebar.service.ts
index 4eebdf6ce..2ee9d55f7 100644
--- a/components/automate-ui/src/app/entities/layout/layout-sidebar.service.ts
+++ b/components/automate-ui/src/app/entities/layout/layout-sidebar.service.ts
@@ -71,7 +71,7 @@ export class LayoutSidebarService {
             },
             {
               name: 'Chef Servers',
-              icon: 'storage',
+              customIcon: 'chef-servers-icon',
               route: '/infrastructure/chef-servers',
               authorized: {
                 anyOf: [['/api/v0/infra/servers', 'get']]
diff --git a/components/automate-ui/src/app/entities/layout/layout.model.ts b/components/automate-ui/src/app/entities/layout/layout.model.ts
index a3b5b1f91..8db9a0e8c 100644
--- a/components/automate-ui/src/app/entities/layout/layout.model.ts
+++ b/components/automate-ui/src/app/entities/layout/layout.model.ts
@@ -39,7 +39,8 @@ export interface MenuItemGroup {
 
 export interface MenuItem {
   name: string;
-  icon: string;
+  icon?: string;
+  customIcon?: string;
   iconRotation?: number;
   route: string;
   authorized?: Authorized;
diff --git a/components/automate-ui/src/app/helpers/auth/regex.ts b/components/automate-ui/src/app/helpers/auth/regex.ts
index 2373d5725..cb8952789 100644
--- a/components/automate-ui/src/app/helpers/auth/regex.ts
+++ b/components/automate-ui/src/app/helpers/auth/regex.ts
@@ -24,7 +24,6 @@ export class Regex {
 
     // Allows valid FQDN only
     VALID_FQDN: /^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-zA-Z0-9_]+([\-\.]{1}[a-zA-Z0-9]+)*\.[a-zA-Z]{2,5}(:[0-9]{1,5})?(\/.*)?$/,
-    VALID_DATA_FEED_URL: /^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-zA-Z0-9_]+([\-\.]{1}[a-zA-Z0-9]+)*\.[a-zA-Z]{2,5}(:[0-9]{1,5})?(\/.*)?$/,
     // Allows valid IP Address only (ipv4)
     VALID_IP_ADDRESS: '^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$'
   };
diff --git a/components/automate-ui/src/app/modules/desktop/check-in-time-series/check-in-time-series.component.html b/components/automate-ui/src/app/modules/desktop/check-in-time-series/check-in-time-series.component.html
index f8b3eff81..7385aba99 100644
--- a/components/automate-ui/src/app/modules/desktop/check-in-time-series/check-in-time-series.component.html
+++ b/components/automate-ui/src/app/modules/desktop/check-in-time-series/check-in-time-series.component.html
@@ -1,31 +1,27 @@
 <div class="content-container">
-  <div class="container">
-    <main>
-      <div class="page-title">Last Check-in by Day</div>
-      <div class="update-time">Updated {{ lastUpdatedMessage }}</div>
-      <div>
-        <select (change)="dateChanged($event.target.value)">
-          <option *ngFor="let selectableDaysAgo of selectableDaysAgoCollection"
-            [value]="selectableDaysAgo" [selected]="selectableDaysAgo == selectedDaysAgo">
-            Last {{ selectableDaysAgo }} Days
-          </option>
-        </select>
-      </div>
-
-      <table>
-        <thead>
-          <tr >
-            <th>Days Ago</th>
-            <th>Percentage of Total</th>
-          </tr>
-        </thead>
-        <tbody>
-          <tr *ngFor="let day of days">
-            <td>{{ day.daysAgo }}</td>
-            <td>{{ day.percentage | number:'1.0-0' }}%</td>
-          </tr>
-        </tbody>
-      </table>
-    </main>
+  <div class="header">
+    <div class="heading">
+      <h2 class="heading-title">Check-in History</h2>
+      <h6 class="heading-time">Updated {{ lastUpdatedMessage }}</h6>
+    </div>
+    <select (change)="dateChanged($event.target.value)">
+      <option *ngFor="let selectableDaysAgo of selectableDaysAgoCollection" [value]="selectableDaysAgo"
+        [selected]="selectableDaysAgo == selectedDaysAgo">
+        Last {{ selectableDaysAgo }} Days
+      </option>
+    </select>
   </div>
+
+  <chef-table>
+    <chef-tbody>
+      <chef-tr *ngFor="let day of days">
+        <chef-td [ngSwitch]="day.daysAgo">
+          <ng-container *ngSwitchCase="0">Today</ng-container>
+          <ng-container *ngSwitchCase="1">Yesterday</ng-container>
+          <ng-container *ngSwitchDefault>{{ day.daysAgo }} days ago</ng-container>
+        </chef-td>
+        <chef-td>{{ day.percentage | number:'1.0-0' }}%</chef-td>
+      </chef-tr>
+    </chef-tbody>
+  </chef-table>
 </div>
diff --git a/components/automate-ui/src/app/modules/desktop/check-in-time-series/check-in-time-series.component.scss b/components/automate-ui/src/app/modules/desktop/check-in-time-series/check-in-time-series.component.scss
index 0e2602f88..6d204c91a 100644
--- a/components/automate-ui/src/app/modules/desktop/check-in-time-series/check-in-time-series.component.scss
+++ b/components/automate-ui/src/app/modules/desktop/check-in-time-series/check-in-time-series.component.scss
@@ -1,15 +1,14 @@
 @import "~styles/variables";
 
-.page-title {
+.heading-title {
   color: var(--chef-primary-dark);
   margin-top: 0;
-  margin-bottom: 12px;
   font-weight: inherit;
   font-style: inherit;
   font-size: 18px;
 }
 
-.update-time {
+.heading-time {
   color: var(--chef-primary-dark);
   margin-top: 0;
   margin-bottom: 12px;
@@ -18,14 +17,16 @@
   font-size: 10px;
 }
 
-.content-container {
-  margin: 10px;
-  padding: 10px;
-  font-family: Muli;
-  font-size: 13px;
-  font-weight: 100;
-  width: 500px;
-  background-color: var(--chef-badge-primary-border);
-  border: 1px solid $chef-blue-medium;
-  border-radius: 4px;
+.header {
+  display: flex;
+  align-items: flex-start;
+  justify-content: space-between;
+
+  select {
+    display: block;
+  }
+}
+
+chef-tr:last-child {
+  margin-bottom: 0;
 }
diff --git a/components/automate-ui/src/app/modules/desktop/daily-check-in/daily-check-in.component.html b/components/automate-ui/src/app/modules/desktop/daily-check-in/daily-check-in.component.html
index 6b7f2ea78..6d03da137 100644
--- a/components/automate-ui/src/app/modules/desktop/daily-check-in/daily-check-in.component.html
+++ b/components/automate-ui/src/app/modules/desktop/daily-check-in/daily-check-in.component.html
@@ -1,30 +1,23 @@
 <div class="content-container">
-  <div class="container">
-    <main>
-      <div class="page-title">Daily Check-in</div>
-      <div class="update-time">Updated {{ lastUpdatedMessage }}</div>
-      <div>
-        <div class="item">
-          <div class="title">Unknown %</div>
-          <div class="total">{{ unknownPercentage | number:'1.0-0' }}%</div>
-        </div>
-        <div class="item">
-          <div class="title">Checked-in %</div>
-          <div class="total">{{ checkedInPercentage | number:'1.0-0' }}%</div>
-        </div>
-        <div class="item">
-          <div class="title">Checked-in Count</div>
-          <div class="total">{{ checkedInCount }}</div>
-        </div>
-        <div class="item">
-          <div class="title">Unknown Count</div>
-          <div class="total">{{ unknownCount }}</div>
-        </div>
-        <div class="item">
-          <div class="title">Total Count</div>
-          <div class="total">{{ totalCount }}</div>
-        </div>
-      </div>
-    </main>
+  <div class="header">
+    <div class="heading">
+      <h2 class="heading-title">Daily Check-in</h2>
+      <h6 class="heading-time">Updated {{ lastUpdatedMessage }}</h6>
+    </div>
+    <app-chart-progress-bar size="large" [value]="unknownCount" [max]="totalCount"></app-chart-progress-bar>
   </div>
+  <chef-phat-radio>
+    <chef-option value='unknown' tabindex="0">
+      <div class="title">Unknown</div>
+      <div class="total">{{ unknownCount }}</div>
+    </chef-option>
+    <chef-option value='checked-in' tabindex="0">
+      <div class="title">Checked-in</div>
+      <div class="total">{{ checkedInCount }}</div>
+    </chef-option>
+    <chef-option value='total' tabindex="0">
+      <div class="title">Total</div>
+      <div class="total">{{ totalCount }}</div>
+    </chef-option>
+  </chef-phat-radio>
 </div>
diff --git a/components/automate-ui/src/app/modules/desktop/daily-check-in/daily-check-in.component.scss b/components/automate-ui/src/app/modules/desktop/daily-check-in/daily-check-in.component.scss
index b89d1a9d4..746e6be20 100644
--- a/components/automate-ui/src/app/modules/desktop/daily-check-in/daily-check-in.component.scss
+++ b/components/automate-ui/src/app/modules/desktop/daily-check-in/daily-check-in.component.scss
@@ -1,15 +1,15 @@
 @import "~styles/variables";
+@import "@carbon/layout/scss/layout";
 
-.page-title {
+.heading-title {
   color: var(--chef-primary-dark);
   margin-top: 0;
-  margin-bottom: 12px;
   font-weight: inherit;
   font-style: inherit;
   font-size: 18px;
 }
 
-.update-time {
+.heading-time {
   color: var(--chef-primary-dark);
   margin-top: 0;
   margin-bottom: 12px;
@@ -18,19 +18,58 @@
   font-size: 10px;
 }
 
-.content-container {
-  margin: 10px;
-  padding: 10px;
-  font-family: Muli;
-  font-size: 13px;
-  font-weight: 100;
-  width: 500px;
-  background-color: $chef-med-grey;
-  border: 1px solid $chef-blue-medium;
-  border-radius: 4px;
+app-chart-progress-bar {
+  margin: $spacing-07 0 $spacing-09 0;
 }
 
-.item {
-  margin: 8px;
-  padding: 3px;
+chef-phat-radio {
+  display: flex;
+
+  @include carbon--breakpoint-down(md) {
+    flex-direction: column;
+
+    chef-option {
+      margin: 0 0 $spacing-03 0;
+
+      &:last-child {
+        margin-bottom: 0;
+      }
+    }
+  }
+
+  chef-option {
+    display: flex;
+    flex: 1;
+    height: auto;
+    width: auto;
+    box-shadow: none;
+    border: 1px solid $chef-grey;
+
+    &:last-child {
+      margin-right: 0;
+    }
+
+    &::before {
+      display: none;
+    }
+
+    .title {
+      font-size: 14px;
+      font-weight: bold;
+      margin-bottom: 4px;
+    }
+
+    .total {
+      font-size: 32px;
+      font-weight: bold;
+
+      &::after {
+        content: 'Desktop';
+        padding-left: 4px;
+        font-size: 12px;
+        font-weight: normal;
+        text-transform: uppercase;
+      }
+    }
+  }
 }
diff --git a/components/automate-ui/src/app/modules/desktop/dashboard/dashboard.component.html b/components/automate-ui/src/app/modules/desktop/dashboard/dashboard.component.html
index 6e25ae274..4ec82172f 100644
--- a/components/automate-ui/src/app/modules/desktop/dashboard/dashboard.component.html
+++ b/components/automate-ui/src/app/modules/desktop/dashboard/dashboard.component.html
@@ -1,41 +1,91 @@
 <div class="content-container">
-  <div class="container">
-    <main>
-      <app-insight
-        [visible] = "insightVisible"
-        [desktops] = "desktops$ | async"
-        [totalDesktops] = "totalDesktopCount$ | async"
-        [pageSize] = "pageSize$ | async"
-        [currentPage] = "currentPage$ | async"
-        [termFilters] = "termFilters$ | async"
-        (pageChange)="onPageChange($event)"
-        (termFilterSelected)="onTermFilterSelected($event)"
-        (closed)="insightClose()"
-        (sortChange)="onSortChange($event)">
-      </app-insight>
-      <app-top-errors
-        [topErrorsItems] = "topErrorsItems$ | async"
-        [lastUpdated] = "topErrorsUpdated$ | async"
-        (errorSelected)="onErrorSelected($event)">
-      </app-top-errors>
-      <app-daily-check-in
-        [unknownPercentage] = "unknownPercentage$ | async"
-        [checkedInPercentage] = "checkedInPercentage$ | async"
-        [totalCount] = "totalCount$ | async"
-        [unknownCount] = "unknownCount$ | async"
-        [lastUpdated] = "checkInCountCollectedUpdated$ | async"
-        [checkedInCount] = "checkedInCount$ | async">
-      </app-daily-check-in>
-      <app-check-in-time-series
-        [days] = "days$ | async"
-        [selectedDaysAgo] = "selectedDaysAgo$ | async"
-        [lastUpdated] = "checkInCountCollectedUpdated$ | async"
-        (daysAgoChanged)="handleDaysAgoChange($event)">
-      </app-check-in-time-series>
-      <app-unknown-desktop-duration-counts
-        [countedDurationItems] = "unknownDesktopCountedDurationItems$ | async"
-        [lastUpdated] = "unknownDesktopCountedDurationUpdated$ | async">
-      </app-unknown-desktop-duration-counts>
-    </main>
+  <div
+    class="dashboard-grid"
+    [ngClass]="{
+      'desktop-list-visible': desktopListVisible,
+      'desktop-list-fullscreened': desktopListFullscreened,
+      'desktop-detail-visible': desktopDetailVisible,
+      'desktop-detail-fullscreened': desktopDetailFullscreened
+    }">
+    <div class="dashboard-grid-row">
+      <div
+        *ngIf="!desktopListFullscreened && !desktopDetailVisible"
+        class="dashboard-overview-column">
+        <div class="dashboard-overview-grid">
+          <div class="dashboard-overview-row">
+            <div class="dashboard-overview-header">
+              <h1>
+                <chef-icon>dashboard</chef-icon> Desktop Management Dashboard
+              </h1>
+            </div>
+          </div>
+          <div class="dashboard-overview-row">
+            <div class="dashboard-overview-col">
+              <app-daily-check-in
+                [unknownPercentage]="unknownPercentage$ | async"
+                [checkedInPercentage]="checkedInPercentage$ | async"
+                [totalCount]="totalCount$ | async"
+                [unknownCount]="unknownCount$ | async"
+                [lastUpdated]="checkInCountCollectedUpdated$ | async"
+                [checkedInCount]="checkedInCount$ | async">
+              </app-daily-check-in>
+            </div>
+            <div class="dashboard-overview-col">
+              <app-check-in-time-series
+                [days]="days$ | async"
+                [selectedDaysAgo]="selectedDaysAgo$ | async"
+                [lastUpdated]="checkInCountCollectedUpdated$ | async"
+                (daysAgoChanged)="handleDaysAgoChange($event)">
+              </app-check-in-time-series>
+            </div>
+          </div>
+          <div class="dashboard-overview-row">
+            <div class="dashboard-overview-col">
+              <app-unknown-desktop-duration-counts
+                [countedDurationItems]="unknownDesktopCountedDurationItems$ | async"
+                [lastUpdated]="unknownDesktopCountedDurationUpdated$ | async"
+                (durationSelected)="onDurationSelected($event)">
+              </app-unknown-desktop-duration-counts>
+            </div>
+            <div class="dashboard-overview-col">
+              <app-top-errors
+                [topErrorsItems]="topErrorsItems$ | async"
+                [lastUpdated]="topErrorsUpdated$ | async"
+                (errorSelected)="onErrorSelected($event)">
+              </app-top-errors>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div
+        *ngIf="desktopListVisible && !desktopDetailFullscreened"
+        class="dashboard-list-column">
+        <app-insight
+          [desktops]="desktops$ | async"
+          [selectedDesktop]="selectedDesktop"
+          [totalDesktops]="totalDesktopCount$ | async"
+          [pageSize]="pageSize$ | async"
+          [currentPage]="currentPage$ | async"
+          [termFilters]="termFilters$ | async"
+          [fullscreened]="desktopListFullscreened"
+          (pageChange)="onPageChange($event)"
+          (termFilterSelected)="onTermFilterSelected($event)"
+          (desktopSelected)="onDesktopSelected($event)"
+          (closed)="onDesktopListClose()"
+          (fullscreenToggled)="onDesktopListFullscreen()"
+          (sortChange)="onSortChange($event)">
+        </app-insight>
+      </div>
+      <div
+        *ngIf="desktopDetailVisible && !desktopListFullscreened"
+        class="dashboard-detail-column">
+        <app-desktop-detail
+          [desktop]="selectedDesktop"
+          [fullscreened]="desktopDetailFullscreened"
+          (closed)="onDesktopDetailClose()"
+          (fullscreenToggled)="onDesktopDetailFullscreen()">
+        </app-desktop-detail>
+      </div>
+    </div>
   </div>
 </div>
diff --git a/components/automate-ui/src/app/modules/desktop/dashboard/dashboard.component.scss b/components/automate-ui/src/app/modules/desktop/dashboard/dashboard.component.scss
index b82833c51..1d66978a6 100644
--- a/components/automate-ui/src/app/modules/desktop/dashboard/dashboard.component.scss
+++ b/components/automate-ui/src/app/modules/desktop/dashboard/dashboard.component.scss
@@ -1,2 +1,168 @@
 @import "~styles/variables";
+@import "@carbon/layout/scss/layout";
+@import "@carbon/grid/scss/grid";
 
+:host {
+  display: block;
+  height: 100%;
+}
+
+h1 {
+  display: flex;
+  margin: $spacing-09 0 0 0;
+  align-items: center;
+  font-size: 18px;
+
+  chef-icon {
+    margin-right: $spacing-03;
+  }
+}
+
+.dashboard-grid {
+  @extend .bx--grid;
+  height: 100%;
+}
+
+.dashboard-grid-row {
+  @extend .bx--row;
+  height: 100%;
+}
+
+.dashboard-overview-column {
+  @extend .bx--col-lg-16;
+}
+
+.desktop-list-visible .dashboard-overview-column {
+  @extend .bx--col-lg-8;
+  height: 100%;
+  overflow: auto;
+
+  @include carbon--breakpoint-down(lg) {
+    height: auto;
+  }
+}
+
+.dashboard-overview-grid {
+  @extend .bx--grid;
+  @extend .bx--no-gutter;
+}
+
+.dashboard-overview-row {
+  @extend .bx--row;
+}
+
+.dashboard-overview-col {
+  @extend .bx--col-lg-8;
+}
+
+.desktop-list-visible .dashboard-overview-col {
+  @extend .bx--col-lg-16;
+}
+
+.dashboard-overview-header {
+  @extend .bx--col-lg-16;
+}
+
+.dashboard-list-column {
+  @extend .bx--col-lg-8;
+  height: 100%;
+  overflow: auto;
+
+  @include carbon--breakpoint-down(lg) {
+    height: auto;
+  }
+}
+
+.desktop-list-fullscreened .dashboard-list-column {
+  @extend .bx--col-lg-16;
+}
+
+.dashboard-detail-column {
+  @extend .bx--col-lg-8;
+  height: 100%;
+  overflow: auto;
+
+  @include carbon--breakpoint-down(lg) {
+    height: auto;
+  }
+}
+
+.desktop-detail-fullscreened .dashboard-detail-column {
+  @extend .bx--col-lg-16;
+}
+
+app-top-errors,
+app-daily-check-in,
+app-check-in-time-series,
+app-unknown-desktop-duration-counts,
+app-insight,
+app-desktop-detail {
+  display: block;
+  margin: $spacing-05 0;
+  padding: $spacing-05;
+  background: $chef-white;
+  border: 1px solid $chef-grey;
+  border-radius: $global-radius;
+
+  @include carbon--breakpoint-up(lg) {
+    height: calc(100% - #{$spacing-07});
+  }
+}
+
+app-top-errors,
+app-unknown-desktop-duration-counts {
+  margin-bottom: $spacing-07;
+
+  @include carbon--breakpoint-up(lg) {
+    height: calc(100% - #{$spacing-09});
+  }
+}
+
+app-unknown-desktop-duration-counts {
+  @include carbon--breakpoint-down(lg) {
+    margin-bottom: $spacing-05;
+  }
+}
+
+.desktop-list-visible,
+.desktop-detail-visible {
+  app-top-errors,
+  app-daily-check-in,
+  app-check-in-time-series,
+  app-unknown-desktop-duration-counts,
+  app-insight,
+  app-desktop-detail {
+    height: auto;
+  }
+
+  app-insight,
+  app-desktop-detail {
+    margin: $spacing-07 0;
+  }
+
+  app-top-errors,
+  app-unknown-desktop-duration-counts {
+    height: auto;
+    margin-bottom: $spacing-05;
+  }
+
+  app-top-errors {
+    margin-bottom: $spacing-07;
+
+    @include carbon--breakpoint-down(lg) {
+      margin-bottom: 0;
+    }
+  }
+
+  app-desktop-detail {
+    @include carbon--breakpoint-down(lg) {
+      margin-top: 0;
+    }
+  }
+}
+
+.desktop-detail-fullscreened {
+  app-desktop-detail {
+    margin-top: $spacing-07;
+  }
+}
diff --git a/components/automate-ui/src/app/modules/desktop/dashboard/dashboard.component.ts b/components/automate-ui/src/app/modules/desktop/dashboard/dashboard.component.ts
index 1efbfe005..272333b3b 100644
--- a/components/automate-ui/src/app/modules/desktop/dashboard/dashboard.component.ts
+++ b/components/automate-ui/src/app/modules/desktop/dashboard/dashboard.component.ts
@@ -60,7 +60,11 @@ export class DashboardComponent implements OnInit {
   public currentPage$: Observable<number>;
   public pageSize$: Observable<number>;
   public termFilters$: Observable<TermFilter[]>;
-  public insightVisible = false;
+  public desktopListVisible = false;
+  public desktopListFullscreened = false;
+  public desktopDetailVisible = false;
+  public desktopDetailFullscreened = false;
+  public selectedDesktop: Desktop;
 
   constructor(
     private store: Store<NgrxStateAtom>,
@@ -161,8 +165,27 @@ export class DashboardComponent implements OnInit {
     this.store.dispatch(new SetDaysAgoSelected({daysAgo}));
   }
 
-  insightClose() {
-    this.insightVisible = false;
+  onDesktopListClose() {
+    this.onDesktopDetailClose();
+    this.desktopListFullscreened = false;
+    this.desktopListVisible = false;
+  }
+
+  onDesktopListFullscreen() {
+    this.desktopListFullscreened = !this.desktopListFullscreened;
+    this.desktopDetailVisible = !this.desktopDetailVisible;
+  }
+
+  onDesktopDetailClose() {
+    this.selectedDesktop = undefined;
+    this.desktopDetailFullscreened = false;
+    this.desktopDetailVisible = false;
+    this.desktopListVisible = true;
+  }
+
+  onDesktopDetailFullscreen() {
+    this.desktopDetailFullscreened = !this.desktopDetailFullscreened;
+    this.desktopListVisible = !this.desktopListVisible;
   }
 
   public onPageChange(pageNumber: number) {
@@ -174,7 +197,18 @@ export class DashboardComponent implements OnInit {
       { type: Terms.ErrorMessage, value: errorItem.message },
       { type: Terms.ErrorType, value: errorItem.type }];
     this.store.dispatch(new UpdateDesktopFilterTerm({ terms }));
-    this.insightVisible = true;
+    this.desktopListVisible = true;
+  }
+
+  public onDurationSelected(_durationItem: any): void {
+    this.store.dispatch(new UpdateDesktopFilterCurrentPage({ page: 1 }));
+    this.desktopListVisible = true;
+  }
+
+  public onDesktopSelected(desktop: Desktop) {
+    this.selectedDesktop = desktop;
+    this.desktopDetailVisible = true;
+    this.desktopListFullscreened = false;
   }
 
   public onTermFilterSelected(term: TermFilter): void {
diff --git a/components/automate-ui/src/app/modules/desktop/desktop-detail/desktop-detail.component.html b/components/automate-ui/src/app/modules/desktop/desktop-detail/desktop-detail.component.html
new file mode 100644
index 000000000..02bfefab2
--- /dev/null
+++ b/components/automate-ui/src/app/modules/desktop/desktop-detail/desktop-detail.component.html
@@ -0,0 +1,207 @@
+<div class="content-container">
+  <div class="header">
+    <div class="heading">
+      <h2 class="heading-title">{{ desktop.name }}</h2>
+    </div>
+    <div class="heading-actions">
+      <chef-button secondary (click)="fullscreenToggled.emit()">
+        <chef-icon *ngIf="!fullscreened">fullscreen</chef-icon>
+        <chef-icon *ngIf="fullscreened">fullscreen_exit</chef-icon>
+      </chef-button>
+      <chef-button secondary (click)="close()">
+        <chef-icon>close</chef-icon>
+      </chef-button>
+    </div>
+  </div>
+
+  <div class="checkin-history">
+    <div class="checkin-history-header">
+      <h3>Check-in History <span *ngIf="showCheckinDebug">(debug)</span></h3>
+      <div class="heading-actions">
+        <chef-button secondary (click)="showCheckinDebug = !showCheckinDebug">
+          <chef-icon>settings</chef-icon>
+        </chef-button>
+        <chef-button secondary (click)="checkinNumDays = (checkinNumDays === 14 ? 28 : 14)">
+          <span>Last {{ (checkinNumDays === 14 ? 2 : 4) }} Weeks</span>
+          <chef-icon>expand_more</chef-icon>
+        </chef-button>
+        <chef-button secondary>
+          <span>Download</span>
+          <chef-icon>expand_more</chef-icon>
+        </chef-button>
+      </div>
+    </div>
+    <div class="debug-actions" *ngIf="showCheckinDebug">
+      <div class="button-toggle">
+        <chef-button secondary (click)="checkinTableType = 'grid'">
+          <chef-icon>view_module</chef-icon>
+        </chef-button>
+        <chef-button secondary (click)="checkinTableType = 'list'">
+          <chef-icon>view_list</chef-icon>
+        </chef-button>
+        <chef-button secondary (click)="checkinTableType = 'json'">
+          <chef-icon>code</chef-icon>
+        </chef-button>
+        <span>{{ checkinTableType }}</span>
+      </div>
+
+      <div class="button-toggle">
+        <span>{{ checkinGridFlexType }}</span>
+        <chef-button secondary (click)="checkinGridFlexType = 'wrap'">
+          <chef-icon>wrap_text</chef-icon>
+        </chef-button>
+        <chef-button secondary (click)="checkinGridFlexType = 'stretch'">
+          <chef-icon>dehaze</chef-icon>
+        </chef-button>
+      </div>
+    </div>
+    <chef-table class="checkin-history-table {{showCheckinDebug ? 'outline' : ''}}" [ngClass]="[checkinTableType, checkinGridFlexType]" *ngIf="checkinTableType !== 'json'">
+      <chef-thead>
+        <chef-tr>
+          <chef-th class="status-cell">Status</chef-th>
+          <chef-th class="date-cell">Date</chef-th>
+          <chef-th class="relative-date-cell"></chef-th>
+        </chef-tr>
+      </chef-thead>
+      <chef-tbody>
+        <chef-tr *ngFor="let item of checkinHistory; index as i">
+          <chef-td class="status-cell" [id]="'checkin-item-'+i+'text'">
+            <chef-icon>{{ item.icon }}</chef-icon>
+            <span>{{ item.text | titlecase }}</span>
+            <chef-tooltip
+              [ngClass]="['checkin-tooltip', item.text]"
+              [attr.for]="'checkin-item-'+i+'text'">
+              <p class="checkin-tooltip-info">
+                <span class="checkin-tooltip-text">{{ item.text | titlecase }}</span>
+                <span class="checkin-tooltip-date">{{ item.date | datetime: DateTime.RFC2822 }}</span>
+              </p>
+              <a class="checkin-tooltip-link" href="">View Client Run Details <chef-icon>open_in_new</chef-icon></a>
+            </chef-tooltip>
+          </chef-td>
+          <chef-td class="date-cell">
+            <span>{{ item.date | datetime: DateTime.RFC2822 }}</span>
+          </chef-td>
+          <chef-td class="relative-date-cell" [ngSwitch]="i">
+            <span *ngSwitchCase="0">Today</span>
+            <span *ngSwitchCase="7">1 week ago</span>
+            <span *ngSwitchDefault>{{ ceil(i / 7) }} weeks ago</span>
+          </chef-td>
+        </chef-tr>
+      </chef-tbody>
+    </chef-table>
+    <chef-snippet
+      *ngIf="checkinTableType === 'json'"
+      class="checkin-history-json"
+      [code]="checkinHistory | json"
+      lang="json">
+    </chef-snippet>
+  </div>
+
+  <div class="detail-info">
+    <h3>Overview</h3>
+    <chef-table>
+      <chef-tbody>
+        <chef-tr>
+          <chef-th>Host Name</chef-th>
+          <chef-td>{{ desktop.name }}</chef-td>
+        </chef-tr>
+        <chef-tr>
+          <chef-th>Chef Infra Server</chef-th>
+          <chef-td>fakechefserver.eastus.cloudapp.azure.com</chef-td>
+        </chef-tr>
+        <chef-tr>
+          <chef-th>Node ID</chef-th>
+          <chef-td>{{ desktop.id }}</chef-td>
+        </chef-tr>
+        <chef-tr>
+          <chef-th>Platform Version</chef-th>
+          <chef-td>{{ desktop.platform }}</chef-td>
+        </chef-tr>
+        <chef-tr>
+          <chef-th>Total Memory</chef-th>
+          <chef-td>16.64GB</chef-td>
+        </chef-tr>
+        <chef-tr>
+          <chef-th>Domain</chef-th>
+          <chef-td>chef.io</chef-td>
+        </chef-tr>
+        <chef-tr>
+          <chef-th>IP Address</chef-th>
+          <chef-td>192.168.5.30</chef-td>
+        </chef-tr>
+        <chef-tr>
+          <chef-th>IPV6 Address</chef-th>
+          <chef-td>fe80:20c:29ff:fe2a:76e5</chef-td>
+        </chef-tr>
+        <chef-tr>
+          <chef-th>MAC Address</chef-th>
+          <chef-td>00:0C:29:2A:76:E5</chef-td>
+        </chef-tr>
+        <chef-tr>
+          <chef-th>Uptime</chef-th>
+          <chef-td>{{ desktop.uptimeSeconds }}</chef-td>
+        </chef-tr>
+        <chef-tr>
+          <chef-th>Time Zone</chef-th>
+          <chef-td>GMT</chef-td>
+        </chef-tr>
+      </chef-tbody>
+    </chef-table>
+
+    <h3>Chef Infra Client</h3>
+    <chef-table>
+      <chef-tbody>
+        <chef-tr>
+          <chef-th>Version</chef-th>
+          <chef-td>{{ desktop.chefVersion }}</chef-td>
+        </chef-tr>
+        <chef-tr>
+          <chef-th>Last Run End Time</chef-th>
+          <chef-td>{{ desktop.checkin }}</chef-td>
+        </chef-tr>
+      </chef-tbody>
+    </chef-table>
+
+    <h3>System</h3>
+    <chef-table>
+      <chef-tbody>
+        <chef-tr>
+          <chef-th>Manufacturer</chef-th>
+          <chef-td>LENOVO</chef-td>
+        </chef-tr>
+        <chef-tr>
+          <chef-th>Serial Number</chef-th>
+          <chef-td>R90QS96N</chef-td>
+        </chef-tr>
+      </chef-tbody>
+    </chef-table>
+
+    <h3>Virtualization</h3>
+    <chef-table>
+      <chef-tbody>
+        <chef-tr>
+          <chef-th>System</chef-th>
+          <chef-td>vmware</chef-td>
+        </chef-tr>
+        <chef-tr>
+          <chef-th>Role</chef-th>
+          <chef-td>guest</chef-td>
+        </chef-tr>
+      </chef-tbody>
+    </chef-table>
+
+    <h3>Kernel</h3>
+    <chef-table>
+      <chef-tbody>
+        <chef-tr>
+          <chef-th>Version</chef-th>
+          <chef-td>10.0.18362</chef-td>
+        </chef-tr>
+        <chef-tr>
+          <chef-th>Release</chef-th>
+          <chef-td>10.0.18362</chef-td>
+        </chef-tr>
+      </chef-tbody>
+    </chef-table>
+  </div>
+</div>
diff --git a/components/automate-ui/src/app/modules/desktop/desktop-detail/desktop-detail.component.scss b/components/automate-ui/src/app/modules/desktop/desktop-detail/desktop-detail.component.scss
new file mode 100644
index 000000000..df40ca2a2
--- /dev/null
+++ b/components/automate-ui/src/app/modules/desktop/desktop-detail/desktop-detail.component.scss
@@ -0,0 +1,425 @@
+@import "~styles/variables";
+@import "@carbon/layout/scss/layout";
+
+.heading-title {
+  margin: 0;
+  color: var(--chef-primary-dark);
+  font-weight: inherit;
+  font-style: inherit;
+  font-size: 18px;
+}
+
+.heading-time {
+  color: var(--chef-primary-dark);
+  margin-top: 0;
+  margin-bottom: 12px;
+  font-weight: inherit;
+  font-style: inherit;
+  font-size: 10px;
+}
+
+.header {
+  display: flex;
+  margin: -#{$spacing-05};
+  margin-bottom: $spacing-05;
+  padding: $spacing-03 $spacing-05;
+  align-items: center;
+  justify-content: space-between;
+  border-top-left-radius: $global-radius;
+  border-top-right-radius: $global-radius;
+  border-bottom: 1px solid $chef-grey;
+
+  chef-button {
+    margin: 0;
+  }
+}
+
+h3 {
+  margin-bottom: $spacing-05;
+}
+
+.detail-info {
+  chef-table {
+    border: 1px solid $chef-grey;
+    border-radius: $global-radius;
+    margin-bottom: $spacing-07;
+    overflow: hidden;
+
+    &:last-child {
+      margin-bottom: 0;
+    }
+
+    chef-tr {
+      margin-bottom: 0;
+
+      chef-th,
+      chef-td,
+      chef-th:first-child,
+      chef-td:first-child,
+      chef-th:last-child,
+      chef-td:last-child  {
+        border: none;
+        border-radius: 0;
+        background: $chef-white;
+      }
+
+      chef-th {
+        font-weight: 400;
+        text-transform: uppercase;
+      }
+
+      &:nth-child(odd) {
+        chef-th,
+        chef-td {
+          background: $chef-lightest-grey;
+        }
+      }
+    }
+  }
+}
+
+.checkin-history {
+  margin-bottom: $spacing-07;
+
+  .checkin-history-header {
+    display: flex;
+    align-items: flex-start;
+    justify-content: space-between;
+
+    chef-button {
+      margin: 0;
+    }
+  }
+}
+
+.checkin-history-table.grid {
+  chef-thead {
+    display: none;
+  }
+
+  chef-tbody {
+    display: flex;
+    flex-flow: row wrap;
+  }
+
+  chef-tr {
+    display: flex;
+    margin: 0;
+    flex-flow: column nowrap;
+    width: auto;
+  }
+
+  chef-td {
+    display: flex;
+    justify-content: center;
+    border: none;
+    border-radius: 0;
+    width: auto;
+    overflow: visible;
+    padding: 10px 0;
+
+    > span {
+      display: none;
+    }
+
+    &.date-cell {
+      display: none;
+    }
+
+    &.relative-date-cell {
+      order: 2;
+      position: relative;
+      border-top: 1px solid $chef-grey;
+
+      span {
+        position: absolute;
+        white-space: nowrap;
+        font-size: 12px;
+      }
+    }
+
+    &.status-cell {
+      cursor: pointer;
+
+      > chef-icon {
+        font-size: 26px;
+        padding: 0 2px;
+      }
+    }
+  }
+
+  chef-tr:nth-child(7n + 1) {
+    chef-td {
+      &.relative-date-cell {
+        > span {
+          display: block;
+        }
+
+        &::before {
+          display: block;
+          width: 1px;
+          height: 10px;
+          background: $chef-grey;
+          position: absolute;
+          top: -10px;
+          content: '';
+        }
+      }
+    }
+  }
+
+  chef-tr:first-child {
+    chef-td {
+      &.relative-date-cell {
+        border-top-color: transparent;
+        justify-content: flex-start;
+
+        span {
+          transform: translateX(15px);
+        }
+
+        &::before {
+          transform: translateX(15px);
+        }
+
+        &::after {
+          display: block;
+          position: absolute;
+          background: $chef-grey;
+          height: 1px;
+          width: 50%;
+          top: -1px;
+          left: 50%;
+          content: '';
+        }
+      }
+    }
+  }
+
+  chef-tr:last-child {
+    chef-td {
+      &.relative-date-cell {
+        border-top-color: transparent;
+
+        &::after {
+          display: block;
+          position: absolute;
+          background: $chef-grey;
+          height: 1px;
+          width: 50%;
+          top: -1px;
+          right: 50%;
+          content: '';
+        }
+      }
+    }
+  }
+
+  &.stretch {
+    chef-tbody {
+      flex-flow: row nowrap;
+      justify-content: space-between;
+      position: relative;
+
+      &::before {
+        display: block;
+        height: 1px;
+        background: $chef-grey;
+        position: absolute;
+        left: 15px;
+        right: 15px;
+        bottom: 20px;
+        content: '';
+
+        @include carbon--breakpoint-down(lg) {
+          left: 11px;
+          right: 11px;
+        }
+
+        @include carbon--breakpoint-down(md) {
+          left: 7px;
+          right: 7px;
+        }
+      }
+    }
+
+    chef-tr:first-child {
+      chef-td {
+        &.relative-date-cell {
+          span,
+          &::before {
+            @include carbon--breakpoint-down(lg) {
+              transform: translateX(11px);
+            }
+
+            @include carbon--breakpoint-down(md) {
+              transform: translateX(7px);
+            }
+          }
+        }
+      }
+    }
+
+    chef-tr:last-child {
+      chef-td {
+        &.relative-date-cell {
+          justify-content: flex-end;
+
+          span,
+          &::before {
+            transform: translateX(-15px);
+
+            @include carbon--breakpoint-down(lg) {
+              transform: translateX(-11px);
+            }
+
+            @include carbon--breakpoint-down(md) {
+              transform: translateX(-7px);
+            }
+          }
+        }
+      }
+    }
+
+    chef-td.status-cell {
+      > chef-icon {
+        @include carbon--breakpoint-down(lg) {
+          font-size: 18px;
+        }
+
+        @include carbon--breakpoint-down(md) {
+          font-size: 14px;
+          padding: 0;
+        }
+      }
+    }
+  }
+
+  &.outline {
+    chef-tr {
+      outline: 1px dashed pink;
+      outline-offset: -1px;
+    }
+  }
+}
+
+.checkin-history-table.list {
+  .status-cell {
+    flex-basis: 30%;
+
+    > chef-icon {
+      margin-right: $spacing-05;
+    }
+  }
+
+  .relative-date-cell {
+    flex-basis: 20%;
+    color: transparent;
+  }
+
+  .date-cell {
+    flex-basis: 50%;
+  }
+
+  chef-tr:nth-child(7n + 1) {
+    .relative-date-cell {
+      color: inherit;
+    }
+  }
+
+  .checkin-tooltip {
+    display: none;
+  }
+}
+
+.checkin-history-json {
+  border: 1px solid $chef-grey;
+  border-radius: $global-radius;
+  overflow: hidden;
+}
+
+.checkin-tooltip {
+  border-left: $spacing-03 solid;
+  border-right: $spacing-03 solid transparent;
+  padding-right: $spacing-03;
+
+  &.error {
+    border-left-color: #E61F36;
+  }
+
+  &.converged {
+    border-left-color: #23AE9A;
+  }
+
+  &.unchanged {
+    border-left-color: #23AE9A;
+  }
+
+  &.unknown {
+    border-left-color: $chef-unknown;
+  }
+}
+
+.checkin-tooltip-info,
+.checkin-tooltip-link {
+  margin: 0;
+  font-size: 14px;
+  white-space: nowrap;
+
+  &:last-child {
+    display: block;
+    margin-top: $spacing-05;
+  }
+
+  chef-icon {
+    margin-left: $spacing-03;
+    color: inherit;
+  }
+}
+
+.checkin-tooltip.unknown {
+  .checkin-tooltip-date,
+  .checkin-tooltip-link {
+    display: none;
+  }
+}
+
+.debug-actions {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-bottom: $spacing-05;
+  background: $chef-lightest-grey;
+  overflow: hidden;
+  border-radius: $global-radius;
+  border: 1px solid $chef-grey;
+
+  chef-button {
+    #{--background-color}: $chef-lightest-grey;
+    margin: 0;
+
+    &:hover {
+      #{--background-color}: $chef-light-grey;
+    }
+  }
+
+  .button-toggle {
+    display: flex;
+    align-items: center;
+
+    chef-button {
+      border-radius: 0;
+      border: none;
+      border-right: 1px solid $chef-grey;
+    }
+
+    &:last-child chef-button {
+      border-right: none;
+      border-left: 1px solid $chef-grey;
+    }
+
+    > span {
+      margin: 0 $spacing-03;
+    }
+  }
+}
diff --git a/components/automate-ui/src/app/modules/desktop/desktop-detail/desktop-detail.component.ts b/components/automate-ui/src/app/modules/desktop/desktop-detail/desktop-detail.component.ts
new file mode 100644
index 000000000..c251053a8
--- /dev/null
+++ b/components/automate-ui/src/app/modules/desktop/desktop-detail/desktop-detail.component.ts
@@ -0,0 +1,45 @@
+import { Component, Input, Output, EventEmitter } from '@angular/core';
+import { Desktop } from 'app/entities/desktop/desktop.model';
+import { DateTime } from 'app/helpers/datetime/datetime';
+
+const historyTypes = [
+  { text: 'converged', icon: 'check_box' },
+  { text: 'unchanged', icon: 'indeterminate_check_box' },
+  { text: 'error', icon: 'error' },
+  { text: 'unknown', icon: 'help' }
+];
+
+const checkinHistory = Array.from(new Array(29)).map((_v, i) => {
+  const historyType = historyTypes[Math.floor(Math.random() * historyTypes.length)];
+  const date = new Date();
+  return { ...historyType, date: new Date(date.setDate(date.getDate() - i)) };
+});
+
+@Component({
+  selector: 'app-desktop-detail',
+  templateUrl: './desktop-detail.component.html',
+  styleUrls: ['./desktop-detail.component.scss']
+})
+export class DesktopDetailComponent {
+
+  @Input() desktop: Desktop;
+  @Input() fullscreened = false;
+
+  @Output() closed: EventEmitter<any> = new EventEmitter();
+  @Output() fullscreenToggled: EventEmitter<void> = new EventEmitter();
+
+  public ceil = Math.ceil;
+  public DateTime = DateTime;
+  public showCheckinDebug = false;
+  public checkinTableType = 'grid';
+  public checkinGridFlexType = 'wrap';
+  public checkinNumDays = 14;
+
+  get checkinHistory() {
+    return checkinHistory.slice(0, this.checkinNumDays + 1);
+  }
+
+  public close(): void {
+    this.closed.emit();
+  }
+}
diff --git a/components/automate-ui/src/app/modules/desktop/desktop.module.ts b/components/automate-ui/src/app/modules/desktop/desktop.module.ts
index 9a2bc976c..e28a73f3a 100644
--- a/components/automate-ui/src/app/modules/desktop/desktop.module.ts
+++ b/components/automate-ui/src/app/modules/desktop/desktop.module.ts
@@ -8,6 +8,7 @@ import { ChefPipesModule } from 'app/pipes/chef-pipes.module';
 import { DailyCheckInComponent } from './daily-check-in/daily-check-in.component';
 import { CheckInTimeSeriesComponent } from './check-in-time-series/check-in-time-series.component';
 import { DashboardComponent } from './dashboard/dashboard.component';
+import { DesktopDetailComponent } from './desktop-detail/desktop-detail.component';
 import { TopErrorsComponent } from './top-errors/top-errors.component';
 import {
   UnknownDesktopDurationCountsComponent
@@ -35,6 +36,7 @@ import { DesktopRoutingModule } from './desktop-routing.module';
   declarations: [
     DailyCheckInComponent,
     DashboardComponent,
+    DesktopDetailComponent,
     CheckInTimeSeriesComponent,
     TopErrorsComponent,
     UnknownDesktopDurationCountsComponent,
diff --git a/components/automate-ui/src/app/modules/desktop/insight/insight.component.html b/components/automate-ui/src/app/modules/desktop/insight/insight.component.html
index f60bc7259..3e967fe1b 100644
--- a/components/automate-ui/src/app/modules/desktop/insight/insight.component.html
+++ b/components/automate-ui/src/app/modules/desktop/insight/insight.component.html
@@ -1,39 +1,128 @@
-<div class="content-container" *ngIf="visible">
-  <div class="container">
-    <main>
-      <button (click)="close()" id="x">X</button>
-      <div class="page-title">Insight</div>
-
-      <ul>
-        <li *ngFor="let term of termFilters">
-          <button (click)="termFilterClicked(term)" id="filter-item">
-            {{term.type}}: {{term.value}}
-          </button>
-        </li>
-      </ul>
-      <div>Total: {{totalDesktops}}</div>
-      <table>
-        <thead>
-          <tr >
-            <th (click)="sortOn('name')" class="table-title">DESKTOP NAME</th>
-            <th (click)="sortOn('check-in')" class="table-title">LAST CHECK-IN TIME</th>
-            <th (click)="sortOn('platform')" class="table-title">PLATFORM</th>
-          </tr>
-        </thead>
-        <tbody>
-          <tr *ngFor="let desktop of desktops">
-            <td>{{ desktop.name }}</td>
-            <td><chef-icon>{{ desktop.status | chefStatusIcon }}</chef-icon> {{ desktop.checkin | timeFromNow }}</td>
-            <td>{{ desktop.platform }}</td>
-          </tr>
-        </tbody>
-      </table>
-      <app-page-picker
-        [total]="totalDesktops"
-        [perPage]="pageSize"
-        [page]="currentPage"
-        (pageChanged)="onPageChange($event)">
-      </app-page-picker>
-    </main>
+<div class="content-container">
+  <div class="header">
+    <div class="heading">
+      <h2 class="heading-title">Error: Chef::ExampleError1: Error 1 occurred</h2>
+    </div>
+    <div class="heading-actions">
+      <chef-button secondary (click)="fullscreenToggled.emit()">
+        <chef-icon *ngIf="!fullscreened">fullscreen</chef-icon>
+        <chef-icon *ngIf="fullscreened">fullscreen_exit</chef-icon>
+      </chef-button>
+      <chef-button secondary (click)="close()">
+        <chef-icon>close</chef-icon>
+      </chef-button>
+    </div>
+  </div>
+  <div class="sub-header">
+    <div class="selected-filters">
+      <chef-button secondary *ngFor="let term of termFilters">
+        <chef-icon>close</chef-icon>
+        <span>{{ term.value }}</span>
+      </chef-button>
+    </div>
+    <chef-button secondary>
+      <span>Attributes</span>
+      <chef-icon>expand_more</chef-icon>
+    </chef-button>
   </div>
+  <div class="filters">
+    <div class="filter-group">
+      <div class="filter-group-heading">Top 3 Platforms</div>
+      <div class="filter-group-options">
+        <chef-pill>
+          <chef-checkbox>windows <chef-pill>10</chef-pill>
+          </chef-checkbox>
+        </chef-pill>
+        <chef-pill>
+          <chef-checkbox>ubuntu<chef-pill>60</chef-pill>
+          </chef-checkbox>
+        </chef-pill>
+        <chef-pill>
+          <chef-checkbox>linux<chef-pill>30</chef-pill>
+          </chef-checkbox>
+        </chef-pill>
+      </div>
+    </div>
+    <div class="filter-group">
+      <div class="filter-group-heading">Top 3 Version</div>
+      <div class="filter-group-options">
+        <chef-pill>
+          <chef-checkbox>10.01 <chef-pill>10</chef-pill>
+          </chef-checkbox>
+        </chef-pill>
+        <chef-pill>
+          <chef-checkbox>16.04<chef-pill>60</chef-pill>
+          </chef-checkbox>
+        </chef-pill>
+        <chef-pill>
+          <chef-checkbox>16.02<chef-pill>30</chef-pill>
+          </chef-checkbox>
+        </chef-pill>
+      </div>
+    </div>
+    <div class="filter-group">
+      <div class="filter-group-heading">Top 3 Domain</div>
+      <div class="filter-group-options">
+        <chef-pill>
+          <chef-checkbox>chef.io <chef-pill>10</chef-pill>
+          </chef-checkbox>
+        </chef-pill>
+        <chef-pill>
+          <chef-checkbox>habitat.sh<chef-pill>60</chef-pill>
+          </chef-checkbox>
+        </chef-pill>
+        <chef-pill>
+          <chef-checkbox>opscode.com<chef-pill>30</chef-pill>
+          </chef-checkbox>
+        </chef-pill>
+      </div>
+    </div>
+    <div class="filter-group">
+      <div class="filter-group-heading">Last Run</div>
+      <div class="filter-group-options">
+        <chef-pill>
+          <chef-checkbox>Error <chef-pill>10</chef-pill>
+          </chef-checkbox>
+        </chef-pill>
+      </div>
+    </div>
+  </div>
+
+  <h4>Total: {{totalDesktops}}</h4>
+  <chef-table>
+    <chef-thead>
+      <chef-tr>
+        <chef-th (click)="sortOn('name')">Desktop Name</chef-th>
+        <chef-th (click)="sortOn('check-in')">Last Check-in</chef-th>
+        <chef-th (click)="sortOn('platform')">Platform</chef-th>
+        <chef-th (click)="sortOn('platformVersion')">Platform Version</chef-th>
+        <chef-th (click)="sortOn('domain')">Domain</chef-th>
+      </chef-tr>
+    </chef-thead>
+    <chef-tbody>
+      <chef-tr
+        *ngFor="let desktop of desktops"
+        tabindex="0"
+        [ngClass]="{'selected': selectedDesktop?.id === desktop.id}"
+        (click)="desktopSelected.emit(desktop)"
+        (keyup.enter)="desktopSelected.emit(desktop)">
+        <chef-td>
+          <chef-icon>desktop_windows</chef-icon> {{ desktop.name }}
+        </chef-td>
+        <chef-td>
+          <chef-icon [ngClass]="desktop.status">{{ desktop.status | chefStatusIcon }}</chef-icon>
+          {{ desktop.checkin | timeFromNow }}
+        </chef-td>
+        <chef-td data-label="Platform">{{ desktop.platform }}</chef-td>
+        <chef-td data-label="Platform Version">10.08</chef-td>
+        <chef-td data-label="Domain">sales</chef-td>
+      </chef-tr>
+    </chef-tbody>
+  </chef-table>
+  <app-page-picker
+    [total]="totalDesktops"
+    [perPage]="pageSize"
+    [page]="currentPage"
+    (pageChanged)="onPageChange($event)">
+  </app-page-picker>
 </div>
diff --git a/components/automate-ui/src/app/modules/desktop/insight/insight.component.scss b/components/automate-ui/src/app/modules/desktop/insight/insight.component.scss
index 8c292a477..82418edac 100644
--- a/components/automate-ui/src/app/modules/desktop/insight/insight.component.scss
+++ b/components/automate-ui/src/app/modules/desktop/insight/insight.component.scss
@@ -1,15 +1,15 @@
 @import "~styles/variables";
+@import "@carbon/layout/scss/layout";
 
-.page-title {
+.heading-title {
+  margin: 0;
   color: var(--chef-primary-dark);
-  margin-top: 0;
-  margin-bottom: 12px;
   font-weight: inherit;
   font-style: inherit;
   font-size: 18px;
 }
 
-.update-time {
+.heading-time {
   color: var(--chef-primary-dark);
   margin-top: 0;
   margin-bottom: 12px;
@@ -18,38 +18,142 @@
   font-size: 10px;
 }
 
-.content-container {
-  margin: 10px;
-  padding: 10px;
-  font-family: Muli;
-  font-size: 13px;
-  font-weight: 100;
-  width: 500px;
-  background-color: $chef-med-grey;
-  border: 1px solid $chef-blue-medium;
-  border-radius: 4px;
+.header {
+  display: flex;
+  margin: -#{$spacing-05};
+  margin-bottom: $spacing-05;
+  padding: $spacing-03 $spacing-05;
+  align-items: center;
+  justify-content: space-between;
+  border-top-left-radius: $global-radius;
+  border-top-right-radius: $global-radius;
+  border-bottom: 1px solid $chef-grey;
+
+  chef-button {
+    margin: 0;
+  }
+}
+
+.sub-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+
+  chef-button {
+    margin: 0;
+  }
+}
+
+.selected-filters {
+  chef-button {
+    margin: 0 $spacing-03 0 0;
+  }
+}
+
+.filters {
+  margin: $spacing-05 0;
+
+  .filter-group-heading {
+    font-size: 12px;
+    text-transform: uppercase;
+    color: #394261;
+  }
+
+  .filter-group-options {
+    #{--pill-background-color}: #{$chef-white};
+
+    > chef-pill {
+      display: inline-flex;
+      margin: 0 $spacing-03 $spacing-03 0;
+      padding: $spacing-03;
+      border: 1px solid $chef-grey;
+    }
+
+    chef-checkbox {
+      #{--pill-background-color}: #{$chef-grey};
+
+      chef-pill {
+        margin-left: $spacing-03;
+        padding: 0 $spacing-03;
+      }
+
+      ::ng-deep .check-wrap {
+        border: 1px solid $chef-grey;
+      }
+    }
+  }
 }
 
-.item {
-  margin: 8px;
-  padding: 3px;
+chef-table {
+  chef-icon {
+    margin-right: $spacing-03;
+  }
 }
 
-chef-icon {
-  margin-right: 1em;
+chef-tr:hover {
+  cursor: pointer;
 }
 
-.table-title {
-  text-align: left;
+:host.fullscreened {
+  .filters {
+    display: flex;
+    flex-flow: row wrap;
+  }
+
+  .filter-group {
+    flex: 0 1 50%;
+  }
+
+  chef-tr:hover,
+  chef-tr.selected {
+    chef-th,
+    chef-td {
+      background: $chef-lightest-grey;
+    }
+  }
 }
 
-#x {
-  margin-right: 5px;
-  margin-top: 5px;
-  position: absolute;
-  background: grey;
-  color: black;
-  top: -10px;
-  right: -10px;
-  border: 1px solid black;
+:host:not(.fullscreened) {
+  chef-thead {
+    display: none;
+  }
+
+  chef-tr {
+    flex-flow: row wrap;
+    border: 1px solid $chef-grey;
+    border-radius: $global-radius;
+    padding: 0 $spacing-05;
+    margin-bottom: $spacing-05;
+  }
+
+  chef-tbody chef-tr:hover,
+  chef-tbody chef-tr.selected {
+    background: $chef-lightest-grey;
+  }
+
+  chef-td {
+    flex-basis: 33.33%;
+    border: none;
+    border-radius: 0;
+    padding: $spacing-05 0;
+    font-size: 12px;
+  }
+
+  chef-td[data-label] {
+    flex-direction: column;
+    align-items: flex-start;
+
+    &::before {
+      display: block;
+      text-transform: uppercase;
+      content: attr(data-label) '';
+    }
+  }
+
+  chef-td:nth-child(1),
+  chef-td:nth-child(2) {
+    flex-basis: 50%;
+    border-bottom: 1px solid $chef-grey;
+    font-size: 14px;
+  }
 }
diff --git a/components/automate-ui/src/app/modules/desktop/insight/insight.component.ts b/components/automate-ui/src/app/modules/desktop/insight/insight.component.ts
index abd671cf0..a69414014 100644
--- a/components/automate-ui/src/app/modules/desktop/insight/insight.component.ts
+++ b/components/automate-ui/src/app/modules/desktop/insight/insight.component.ts
@@ -1,4 +1,4 @@
-import { Component, Input, Output, EventEmitter } from '@angular/core';
+import { Component, HostBinding, Input, Output, EventEmitter } from '@angular/core';
 import { Desktop, TermFilter } from 'app/entities/desktop/desktop.model';
 
 @Component({
@@ -8,20 +8,21 @@ import { Desktop, TermFilter } from 'app/entities/desktop/desktop.model';
 })
 export class InsightComponent {
 
-  @Input() visible = false;
   @Input() desktops: Desktop[];
+  @Input() selectedDesktop: Desktop;
   @Input() currentPage: number;
   @Input() pageSize: number;
   @Input() totalDesktops: number;
   @Input() termFilters: TermFilter[];
+  @Input() @HostBinding('class.fullscreened') fullscreened = false;
 
   @Output() closed: EventEmitter<any> = new EventEmitter();
+  @Output() fullscreenToggled: EventEmitter<any> = new EventEmitter();
   @Output() pageChange: EventEmitter<number> = new EventEmitter();
   @Output() termFilterSelected: EventEmitter<TermFilter> = new EventEmitter();
   // Returns 'name', 'check-in', or 'platform'
   @Output() sortChange: EventEmitter<string> = new EventEmitter();
-
-  constructor() { }
+  @Output() desktopSelected: EventEmitter<Desktop> = new EventEmitter();
 
   public close(): void {
     this.closed.emit();
diff --git a/components/automate-ui/src/app/modules/desktop/top-errors/top-errors.component.html b/components/automate-ui/src/app/modules/desktop/top-errors/top-errors.component.html
index 5abfe4628..40bb70334 100644
--- a/components/automate-ui/src/app/modules/desktop/top-errors/top-errors.component.html
+++ b/components/automate-ui/src/app/modules/desktop/top-errors/top-errors.component.html
@@ -1,25 +1,31 @@
 <div class="content-container">
-  <div class="container">
-    <main>
-      <div class="page-title">Top 10 Errors</div>
-      <div class="update-time">Updated {{ lastUpdatedMessage }}</div>
-
-      <table>
-        <thead>
-          <tr >
-            <th>Error Message</th>
-            <th>Number of Desktops</th>
-          </tr>
-        </thead>
-        <tbody>
-          <tr *ngFor="let topErrorsItem of topErrorsItems">
-            <td (click)="onItemClicked(topErrorsItem)">{{topErrorsItem.type}}: {{ topErrorsItem.message }}</td>
-            <td>{{ topErrorsItem.count }}</td>
-            <td></td>
-          </tr>
-        </tbody>
-      </table>
-
-    </main>
-  </div>
+  <h2 class="heading-title">Top 10 Errors</h2>
+  <h6 class="heading-time">Updated {{ lastUpdatedMessage }}</h6>
+  <chef-table class="chart-list">
+    <chef-tbody>
+      <chef-tr
+        *ngFor="let item of topErrorsItems"
+        class="chart-list-item"
+        tabindex="0"
+        (click)="errorSelected.emit(item)"
+        (keyup.enter)="errorSelected.emit(item)">
+        <chef-td class="chart-list-item-label-cell">
+          {{ item.type }}: {{ item.message }}
+        </chef-td>
+        <chef-td class="chart-list-item-count-cell">
+          <span>
+            {{ item.count }}
+            <chef-icon>desktop_windows</chef-icon>
+          </span>
+        </chef-td>
+        <chef-td class="chart-list-item-chart-cell">
+          <app-chart-progress-bar
+            size="small"
+            [value]="item.count"
+            [max]="item.count">
+          </app-chart-progress-bar>
+        </chef-td>
+      </chef-tr>
+    </chef-tbody>
+  </chef-table>
 </div>
diff --git a/components/automate-ui/src/app/modules/desktop/top-errors/top-errors.component.scss b/components/automate-ui/src/app/modules/desktop/top-errors/top-errors.component.scss
index 65ac49b18..94ac4247a 100644
--- a/components/automate-ui/src/app/modules/desktop/top-errors/top-errors.component.scss
+++ b/components/automate-ui/src/app/modules/desktop/top-errors/top-errors.component.scss
@@ -1,27 +1,15 @@
 @import "~styles/variables";
+@import "@carbon/layout/scss/layout";
 
-.page-title {
+.heading-title {
   color: var(--chef-primary-dark);
   margin-top: 0;
-  margin-bottom: 12px;
   font-weight: inherit;
   font-style: inherit;
   font-size: 18px;
 }
 
-.content-container {
-  margin: 10px;
-  padding: 10px;
-  font-family: Muli;
-  font-size: 13px;
-  font-weight: 100;
-  background-color: $chef-med-grey;
-  border: 1px solid $chef-blue-medium;
-  border-radius: 4px;
-  width: 500px;
-}
-
-.update-time {
+.heading-time {
   color: var(--chef-primary-dark);
   margin-top: 0;
   margin-bottom: 12px;
@@ -29,3 +17,50 @@
   font-style: inherit;
   font-size: 10px;
 }
+
+.chart-list-item {
+  &:last-child {
+    margin-bottom: 0;
+  }
+
+  &:hover {
+    cursor: pointer;
+
+    .chart-list-item-label-cell,
+    .chart-list-item-count-cell {
+      background: $chef-lightest-grey;
+    }
+  }
+
+  .chart-list-item-label-cell {
+    flex-basis: 55%;
+  }
+
+  .chart-list-item-count-cell {
+    border-right: 1px solid $chef-grey;
+    border-top-right-radius: $global-radius;
+    border-bottom-right-radius: $global-radius;
+    flex-basis: 20%;
+    justify-content: flex-end;
+
+    span {
+      display: inline-flex;
+      align-items: center;
+    }
+
+    chef-icon {
+      margin-left: $spacing-03;
+    }
+  }
+
+  .chart-list-item-chart-cell {
+    flex-basis: 25%;
+    padding-right: 0;
+    border-color: transparent;
+
+    app-chart-progress-bar {
+      margin: 0;
+      width: 100%;
+    }
+  }
+}
diff --git a/components/automate-ui/src/app/modules/desktop/top-errors/top-errors.component.ts b/components/automate-ui/src/app/modules/desktop/top-errors/top-errors.component.ts
index 2671b0edc..884acbdcd 100644
--- a/components/automate-ui/src/app/modules/desktop/top-errors/top-errors.component.ts
+++ b/components/automate-ui/src/app/modules/desktop/top-errors/top-errors.component.ts
@@ -2,6 +2,7 @@ import { Component, Input, Output, OnInit, OnDestroy, OnChanges,
   SimpleChange, EventEmitter } from '@angular/core';
 import { Subject, timer } from 'rxjs';
 import { takeUntil } from 'rxjs/operators';
+import { maxBy } from 'lodash/fp';
 import { TimeFromNowPipe } from 'app/pipes/time-from-now.pipe';
 
 import {
@@ -45,7 +46,7 @@ export class TopErrorsComponent  implements OnInit, OnDestroy, OnChanges  {
     this.isDestroyed.complete();
   }
 
-  public onItemClicked(item: TopErrorsItem) {
-    this.errorSelected.emit(item);
+  get topErrorsItemsMax() {
+    return maxBy('count', this.topErrorsItems);
   }
 }
diff --git a/components/automate-ui/src/app/modules/desktop/unknown-desktop-duration-counts/unknown-desktop-duration-counts.component.html b/components/automate-ui/src/app/modules/desktop/unknown-desktop-duration-counts/unknown-desktop-duration-counts.component.html
index bbf580bfd..17371fa55 100644
--- a/components/automate-ui/src/app/modules/desktop/unknown-desktop-duration-counts/unknown-desktop-duration-counts.component.html
+++ b/components/automate-ui/src/app/modules/desktop/unknown-desktop-duration-counts/unknown-desktop-duration-counts.component.html
@@ -1,19 +1,31 @@
 <div class="content-container">
-  <div class="container">
-    <main>
-      <div class="page-title">Unknown Duration</div>
-      <div class="update-time">Updated {{ lastUpdatedMessage }}</div>
-
-      <table>
-        <tbody>
-          <tr *ngFor="let items of countedDurationItems">
-            <td>{{ formatDuration(items.duration) }}</td>
-            <td>{{ items.count }}</td>
-            <td></td>
-          </tr>
-        </tbody>
-      </table>
-
-    </main>
-  </div>
+  <h2 class="heading-title">Time since Last Check-in</h2>
+  <h6 class="heading-time">Updated {{ lastUpdatedMessage }}</h6>
+  <chef-table class="chart-list">
+    <chef-tbody>
+      <chef-tr
+        *ngFor="let item of countedDurationItems"
+        class="chart-list-item"
+        tabindex="0"
+        (click)="durationSelected.emit(item)"
+        (keyup.enter)="durationSelected.emit(item)">
+        <chef-td class="chart-list-item-label-cell">
+          {{ formatDuration(item.duration) }}
+        </chef-td>
+        <chef-td class="chart-list-item-count-cell">
+          <span>
+            {{ item.count }}
+            <chef-icon>desktop_windows</chef-icon>
+          </span>
+        </chef-td>
+        <chef-td class="chart-list-item-chart-cell">
+          <app-chart-progress-bar
+            size="small"
+            [value]="item.count"
+            [max]="countedDurationItemsMax.count">
+          </app-chart-progress-bar>
+        </chef-td>
+      </chef-tr>
+    </chef-tbody>
+  </chef-table>
 </div>
diff --git a/components/automate-ui/src/app/modules/desktop/unknown-desktop-duration-counts/unknown-desktop-duration-counts.component.scss b/components/automate-ui/src/app/modules/desktop/unknown-desktop-duration-counts/unknown-desktop-duration-counts.component.scss
index 6d1407b9e..94ac4247a 100644
--- a/components/automate-ui/src/app/modules/desktop/unknown-desktop-duration-counts/unknown-desktop-duration-counts.component.scss
+++ b/components/automate-ui/src/app/modules/desktop/unknown-desktop-duration-counts/unknown-desktop-duration-counts.component.scss
@@ -1,27 +1,15 @@
 @import "~styles/variables";
+@import "@carbon/layout/scss/layout";
 
-.page-title {
+.heading-title {
   color: var(--chef-primary-dark);
   margin-top: 0;
-  margin-bottom: 12px;
   font-weight: inherit;
   font-style: inherit;
   font-size: 18px;
 }
 
-.content-container {
-  margin: 10px;
-  padding: 10px;
-  font-family: Muli;
-  font-size: 13px;
-  font-weight: 100;
-  background-color: var(--chef-badge-primary-border);
-  border: 1px solid $chef-blue-medium;
-  border-radius: 4px;
-  width: 500px;
-}
-
-.update-time {
+.heading-time {
   color: var(--chef-primary-dark);
   margin-top: 0;
   margin-bottom: 12px;
@@ -29,3 +17,50 @@
   font-style: inherit;
   font-size: 10px;
 }
+
+.chart-list-item {
+  &:last-child {
+    margin-bottom: 0;
+  }
+
+  &:hover {
+    cursor: pointer;
+
+    .chart-list-item-label-cell,
+    .chart-list-item-count-cell {
+      background: $chef-lightest-grey;
+    }
+  }
+
+  .chart-list-item-label-cell {
+    flex-basis: 55%;
+  }
+
+  .chart-list-item-count-cell {
+    border-right: 1px solid $chef-grey;
+    border-top-right-radius: $global-radius;
+    border-bottom-right-radius: $global-radius;
+    flex-basis: 20%;
+    justify-content: flex-end;
+
+    span {
+      display: inline-flex;
+      align-items: center;
+    }
+
+    chef-icon {
+      margin-left: $spacing-03;
+    }
+  }
+
+  .chart-list-item-chart-cell {
+    flex-basis: 25%;
+    padding-right: 0;
+    border-color: transparent;
+
+    app-chart-progress-bar {
+      margin: 0;
+      width: 100%;
+    }
+  }
+}
diff --git a/components/automate-ui/src/app/modules/desktop/unknown-desktop-duration-counts/unknown-desktop-duration-counts.component.ts b/components/automate-ui/src/app/modules/desktop/unknown-desktop-duration-counts/unknown-desktop-duration-counts.component.ts
index bdbf7e76c..e23dde131 100644
--- a/components/automate-ui/src/app/modules/desktop/unknown-desktop-duration-counts/unknown-desktop-duration-counts.component.ts
+++ b/components/automate-ui/src/app/modules/desktop/unknown-desktop-duration-counts/unknown-desktop-duration-counts.component.ts
@@ -1,6 +1,7 @@
-import { Component, Input, OnInit, OnDestroy, OnChanges, SimpleChange } from '@angular/core';
+import { Component, Input, OnInit, OnDestroy, OnChanges, Output, EventEmitter, SimpleChange } from '@angular/core';
 import { Subject, timer } from 'rxjs';
 import { takeUntil } from 'rxjs/operators';
+import { maxBy } from 'lodash/fp';
 import { TimeFromNowPipe } from 'app/pipes/time-from-now.pipe';
 
 import {
@@ -17,6 +18,8 @@ export class UnknownDesktopDurationCountsComponent  implements OnInit, OnDestroy
   @Input() countedDurationItems: CountedDurationItem[];
   @Input() lastUpdated: Date;
 
+  @Output() durationSelected: EventEmitter<any> = new EventEmitter();
+
   private isDestroyed = new Subject<boolean>();
   public lastUpdatedMessage = '-';
   private timeFromNowPipe = new TimeFromNowPipe();
@@ -43,6 +46,10 @@ export class UnknownDesktopDurationCountsComponent  implements OnInit, OnDestroy
     this.isDestroyed.complete();
   }
 
+  get countedDurationItemsMax() {
+    return maxBy('count', this.countedDurationItems);
+  }
+
   // Make this more general once we don't have hard coded durations.
   formatDuration(duration: string): string {
     switch (duration) {
diff --git a/components/automate-ui/src/app/modules/infra-proxy/cookbook-details/cookbook-details.component.html b/components/automate-ui/src/app/modules/infra-proxy/cookbook-details/cookbook-details.component.html
index d12ad2676..333826d18 100644
--- a/components/automate-ui/src/app/modules/infra-proxy/cookbook-details/cookbook-details.component.html
+++ b/components/automate-ui/src/app/modules/infra-proxy/cookbook-details/cookbook-details.component.html
@@ -5,7 +5,7 @@
       <chef-breadcrumbs>
         <chef-breadcrumb [link]="['/infrastructure/chef-servers']">Chef Server</chef-breadcrumb>
         <chef-breadcrumb [routerLink]="['/infrastructure/chef-servers', serverId]">Orgs</chef-breadcrumb>
-        <chef-breadcrumb [routerLink]="['/infrastructure/chef-servers', serverId, 'org', orgId]">Cookbooks</chef-breadcrumb>
+        <chef-breadcrumb [routerLink]="['/infrastructure/chef-servers', serverId, 'organizations', orgId]">Cookbooks</chef-breadcrumb>
         {{ cookbookName }}
       </chef-breadcrumbs>
       <chef-page-header>
diff --git a/components/automate-ui/src/app/modules/infra-proxy/cookbooks/cookbooks.component.html b/components/automate-ui/src/app/modules/infra-proxy/cookbooks/cookbooks.component.html
index 940e49142..f4433ec08 100644
--- a/components/automate-ui/src/app/modules/infra-proxy/cookbooks/cookbooks.component.html
+++ b/components/automate-ui/src/app/modules/infra-proxy/cookbooks/cookbooks.component.html
@@ -11,7 +11,7 @@
       <chef-tbody>
         <chef-tr *ngFor="let cookbook of cookbooks">
           <chef-td>
-            <a [routerLink]="['/infrastructure','chef-servers', serverId, 'org', orgId, 'cookbooks', cookbook.name]">{{ cookbook.name }}</a>
+            <a [routerLink]="['/infrastructure','chef-servers', serverId, 'organizations', orgId, 'cookbooks', cookbook.name]">{{ cookbook.name }}</a>
           </chef-td>
           <chef-td>{{ cookbook.version }}</chef-td>
         </chef-tr>
diff --git a/components/automate-ui/src/app/modules/infra-proxy/data-bags-list/data-bags-list.component.html b/components/automate-ui/src/app/modules/infra-proxy/data-bags-list/data-bags-list.component.html
new file mode 100644
index 000000000..3bb125d63
--- /dev/null
+++ b/components/automate-ui/src/app/modules/infra-proxy/data-bags-list/data-bags-list.component.html
@@ -0,0 +1,17 @@
+<section class="databags">
+  <chef-loading-spinner *ngIf="dataBagsListLoading" size="50"></chef-loading-spinner>
+  <ng-container  *ngIf="!dataBagsListLoading">
+    <chef-table>
+      <chef-thead>
+        <chef-tr>
+          <chef-th>Name</chef-th>
+        </chef-tr>
+      </chef-thead>
+      <chef-tbody>
+        <chef-tr *ngFor="let dataBag of dataBags">
+          <chef-td>{{ dataBag.name }}</chef-td>
+        </chef-tr>
+      </chef-tbody>
+    </chef-table>
+  </ng-container>
+</section>
diff --git a/components/automate-ui/src/app/modules/infra-proxy/data-bags-list/data-bags-list.component.scss b/components/automate-ui/src/app/modules/infra-proxy/data-bags-list/data-bags-list.component.scss
new file mode 100644
index 000000000..6bcb8603f
--- /dev/null
+++ b/components/automate-ui/src/app/modules/infra-proxy/data-bags-list/data-bags-list.component.scss
@@ -0,0 +1,8 @@
+@import "~styles/variables";
+
+chef-loading-spinner {
+  display: block;
+  margin: 100px auto;
+  width: 100px;
+  z-index: 199;
+}
diff --git a/components/automate-ui/src/app/modules/infra-proxy/data-bags-list/data-bags-list.component.spec.ts b/components/automate-ui/src/app/modules/infra-proxy/data-bags-list/data-bags-list.component.spec.ts
new file mode 100644
index 000000000..9d4480403
--- /dev/null
+++ b/components/automate-ui/src/app/modules/infra-proxy/data-bags-list/data-bags-list.component.spec.ts
@@ -0,0 +1,60 @@
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+import { StoreModule } from '@ngrx/store';
+import { MockComponent } from 'ng2-mock-component';
+
+import { ngrxReducers, runtimeChecks } from 'app/ngrx.reducers';
+import { FeatureFlagsService } from 'app/services/feature-flags/feature-flags.service';
+import { DataBagsListComponent } from './data-bags-list.component';
+
+describe('DataBagsListComponent', () => {
+  let component: DataBagsListComponent;
+  let fixture: ComponentFixture<DataBagsListComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [
+        MockComponent({ selector: 'a', inputs: ['routerLink'] }),
+        MockComponent({ selector: 'chef-error' }),
+        MockComponent({ selector: 'chef-form-field' }),
+        MockComponent({ selector: 'chef-heading' }),
+        MockComponent({ selector: 'chef-icon' }),
+        MockComponent({ selector: 'chef-loading-spinner' }),
+        MockComponent({ selector: 'chef-page-header' }),
+        MockComponent({ selector: 'chef-subheading' }),
+        MockComponent({ selector: 'chef-toolbar' }),
+        MockComponent({ selector: 'chef-table' }),
+        MockComponent({ selector: 'chef-thead' }),
+        MockComponent({ selector: 'chef-tbody' }),
+        MockComponent({ selector: 'chef-tr' }),
+        MockComponent({ selector: 'chef-th' }),
+        MockComponent({ selector: 'chef-td' }),
+        MockComponent({ selector: 'input', inputs: ['resetOrigin'] }),
+        MockComponent({ selector: 'mat-select' }),
+        MockComponent({ selector: 'mat-option' }),
+        DataBagsListComponent
+      ],
+      providers: [
+        FeatureFlagsService
+      ],
+      imports: [
+        FormsModule,
+        ReactiveFormsModule,
+        RouterTestingModule,
+        StoreModule.forRoot(ngrxReducers, { runtimeChecks })
+      ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(DataBagsListComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/components/automate-ui/src/app/modules/infra-proxy/data-bags-list/data-bags-list.component.ts b/components/automate-ui/src/app/modules/infra-proxy/data-bags-list/data-bags-list.component.ts
new file mode 100644
index 000000000..50085838e
--- /dev/null
+++ b/components/automate-ui/src/app/modules/infra-proxy/data-bags-list/data-bags-list.component.ts
@@ -0,0 +1,53 @@
+import { Component, Input, OnInit } from '@angular/core';
+import { Store } from '@ngrx/store';
+import { combineLatest } from 'rxjs';
+import { filter } from 'rxjs/operators';
+import { isNil } from 'lodash/fp';
+
+import { NgrxStateAtom } from 'app/ngrx.reducers';
+import { EntityStatus } from 'app/entities/entities';
+import { LayoutFacadeService, Sidebar } from 'app/entities/layout/layout.facade';
+import { GetDataBags } from 'app/entities/data-bags/data-bags.action';
+import { DataBags } from 'app/entities/data-bags/data-bags.model';
+import {
+  allDataBags,
+  getAllStatus as getAllDatabagsForOrgStatus
+} from 'app/entities/data-bags/data-bags.selectors';
+
+@Component({
+  selector: 'app-data-bags-list',
+  templateUrl: './data-bags-list.component.html',
+  styleUrls: ['./data-bags-list.component.scss']
+})
+
+export class DataBagsListComponent implements OnInit {
+  @Input() serverId: string;
+  @Input() orgId: string;
+
+  public dataBags: DataBags[];
+  public dataBagsListLoading = true;
+
+  constructor(
+    private store: Store<NgrxStateAtom>,
+    private layoutFacade: LayoutFacadeService
+  ) { }
+
+  ngOnInit() {
+    this.layoutFacade.showSidebar(Sidebar.Infrastructure);
+
+    this.store.dispatch(new GetDataBags({
+      server_id: this.serverId, org_id: this.orgId
+    }));
+
+    combineLatest([
+      this.store.select(getAllDatabagsForOrgStatus),
+      this.store.select(allDataBags)
+    ]).pipe(
+      filter(([getDataBagsSt, allDataBagsState]) =>
+      getDataBagsSt === EntityStatus.loadingSuccess && !isNil(allDataBagsState))
+    ).subscribe(([ _getDataBagsSt, allDataBagsState]) => {
+      this.dataBags = allDataBagsState;
+      this.dataBagsListLoading = false;
+    });
+  }
+}
diff --git a/components/automate-ui/src/app/modules/infra-proxy/infra-proxy.module.ts b/components/automate-ui/src/app/modules/infra-proxy/infra-proxy.module.ts
index daa2d067d..68309614c 100644
--- a/components/automate-ui/src/app/modules/infra-proxy/infra-proxy.module.ts
+++ b/components/automate-ui/src/app/modules/infra-proxy/infra-proxy.module.ts
@@ -11,6 +11,7 @@ import { CookbooksComponent } from './cookbooks/cookbooks.component';
 import { CookbookDetailsComponent } from './cookbook-details/cookbook-details.component';
 import { CreateChefServerModalComponent } from './create-chef-server-modal/create-chef-server-modal.component';
 import { CreateOrgModalComponent } from './create-org-modal/create-org-modal.component';
+import { DataBagsListComponent } from './data-bags-list/data-bags-list.component';
 import { EnvironmentsComponent } from './environments/environments.component';
 import { InfraRolesComponent } from './infra-roles/infra-roles.component';
 import { InfraRoleDetailsComponent } from './infra-role-details/infra-role-details.component';
@@ -27,6 +28,7 @@ import { TreeTableModule } from './tree-table/tree-table.module';
     CookbookDetailsComponent,
     CreateChefServerModalComponent,
     CreateOrgModalComponent,
+    DataBagsListComponent,
     EnvironmentsComponent,
     JsonTreeTableComponent,
     InfraRolesComponent,
diff --git a/components/automate-ui/src/app/modules/infra-proxy/infra-role-details/infra-role-details.component.html b/components/automate-ui/src/app/modules/infra-proxy/infra-role-details/infra-role-details.component.html
index ba3cc6a62..3d237da22 100644
--- a/components/automate-ui/src/app/modules/infra-proxy/infra-role-details/infra-role-details.component.html
+++ b/components/automate-ui/src/app/modules/infra-proxy/infra-role-details/infra-role-details.component.html
@@ -4,7 +4,7 @@
       <chef-breadcrumbs>
         <chef-breadcrumb [link]="['/infrastructure/chef-servers']">Chef Server</chef-breadcrumb>
         <chef-breadcrumb [routerLink]="['/infrastructure/chef-servers', serverId]">Orgs</chef-breadcrumb>
-        <chef-breadcrumb [routerLink]="['/infrastructure/chef-servers', serverId, 'org', orgId]">Roles</chef-breadcrumb>
+        <chef-breadcrumb [routerLink]="['/infrastructure/chef-servers', serverId, 'organizations', orgId]">Roles</chef-breadcrumb>
          {{role?.name}}
       </chef-breadcrumbs>
       <chef-page-header>
diff --git a/components/automate-ui/src/app/modules/infra-proxy/infra-roles/infra-roles.component.html b/components/automate-ui/src/app/modules/infra-proxy/infra-roles/infra-roles.component.html
index 7ec6a6442..2bca0f55b 100644
--- a/components/automate-ui/src/app/modules/infra-proxy/infra-roles/infra-roles.component.html
+++ b/components/automate-ui/src/app/modules/infra-proxy/infra-roles/infra-roles.component.html
@@ -12,9 +12,9 @@
       <chef-tbody>
         <chef-tr *ngFor="let role of roles">
           <chef-td>
-            <a [routerLink]="['/infrastructure','chef-servers', serverId, 'org', orgId, 'roles', role.name]">{{ role.name }}</a>
+            <a [routerLink]="['/infrastructure','chef-servers', serverId, 'organizations', orgId, 'roles', role.name]">{{ role.name }}</a>
           </chef-td>
-          <chef-td>{{ role.description === '' ?  'N/A' : role.description }}</chef-td>
+          <chef-td>{{ role.description === '' ?  '...' : role.description }}</chef-td>
           <chef-td>{{ role.environments.join(", ") }}</chef-td>
         </chef-tr>
       </chef-tbody>
diff --git a/components/automate-ui/src/app/modules/infra-proxy/org-details/org-details.component.html b/components/automate-ui/src/app/modules/infra-proxy/org-details/org-details.component.html
index 47742ef44..f873f9827 100644
--- a/components/automate-ui/src/app/modules/infra-proxy/org-details/org-details.component.html
+++ b/components/automate-ui/src/app/modules/infra-proxy/org-details/org-details.component.html
@@ -33,6 +33,9 @@
           </app-tab>
           <app-tab tabTitle="Environments" #environments_tab  [active]="environmentsTab">
             <app-environments *ngIf="environments_tab.active" [serverId]="serverId" [orgId]="orgId"></app-environments>
+          </app-tab>  
+          <app-tab tabTitle="Data Bags" #data_bags_tab  [active]="dataBagsTab">
+            <app-data-bags-list *ngIf="data_bags_tab.active" [serverId]="serverId" [orgId]="orgId"></app-data-bags-list>
           </app-tab>
           <app-tab tabTitle="Details" #details_tab [active]="false">
             <app-org-edit *ngIf="details_tab.active" [serverId]="serverId" [orgId]="orgId"></app-org-edit> 
diff --git a/components/automate-ui/src/app/modules/infra-proxy/org-details/org-details.component.ts b/components/automate-ui/src/app/modules/infra-proxy/org-details/org-details.component.ts
index f60c4f0d9..23acd4528 100644
--- a/components/automate-ui/src/app/modules/infra-proxy/org-details/org-details.component.ts
+++ b/components/automate-ui/src/app/modules/infra-proxy/org-details/org-details.component.ts
@@ -29,6 +29,7 @@ export class OrgDetailsComponent implements OnInit, OnDestroy {
   public cookbooksTab = true;
   public environmentsTab = false;
   public rolesTab = false;
+  public dataBagsTab = false;
   private isDestroyed = new Subject<boolean>();
 
   previousRoute$: Observable<RouterState>;
@@ -45,6 +46,7 @@ export class OrgDetailsComponent implements OnInit, OnDestroy {
         if ( params.path.includes('roles') ) {
           this.cookbooksTab = false;
           this.environmentsTab = false;
+          this.dataBagsTab = false;
           this.rolesTab = true;
         }
 
@@ -94,6 +96,9 @@ export class OrgDetailsComponent implements OnInit, OnDestroy {
         this.telemetryService.track('orgDetailsTab', 'environments');
         break;
       case 3:
+        this.telemetryService.track('orgDetailsTab', 'dataBags');
+        break;
+      case 4:
         this.telemetryService.track('orgDetailsTab', 'org-edit');
         break;
     }
diff --git a/components/automate-ui/src/app/modules/infra-proxy/tree-table/component/tree-table.component.html b/components/automate-ui/src/app/modules/infra-proxy/tree-table/component/tree-table.component.html
index 21fe03e17..84ff40a57 100644
--- a/components/automate-ui/src/app/modules/infra-proxy/tree-table/component/tree-table.component.html
+++ b/components/automate-ui/src/app/modules/infra-proxy/tree-table/component/tree-table.component.html
@@ -2,7 +2,9 @@
   <div *ngFor="let column of displayedColumns; first as isFirst;">
     <ng-container matColumnDef="{{column}}">
       <th mat-header-cell *matHeaderCellDef [ngClass]="{'vertical-separator': options.verticalSeparator}">
-        {{options.capitalisedHeader ? (column | titlecase) : column}}
+        <p *ngIf="column !== 'name'">
+          {{options.capitalisedHeader ? (column | titlecase) : column}}
+        </p>
       </th>
       <td mat-cell *matCellDef="let element" [ngClass]="{'vertical-separator': options.verticalSeparator}">
         <div *ngIf="isFirst">
diff --git a/components/automate-ui/src/app/modules/infra-proxy/tree-table/component/tree-table.component.scss b/components/automate-ui/src/app/modules/infra-proxy/tree-table/component/tree-table.component.scss
index fb6b5854b..9de577b11 100644
--- a/components/automate-ui/src/app/modules/infra-proxy/tree-table/component/tree-table.component.scss
+++ b/components/automate-ui/src/app/modules/infra-proxy/tree-table/component/tree-table.component.scss
@@ -54,10 +54,6 @@ th.mat-header-cell {
   color: #222435;
 }
 
-th:first-child {
-  color: #FFFFFF;
-}
-
 .mat-elevation-z5 {
   box-shadow: none;
 }
diff --git a/components/automate-ui/src/app/modules/infra-proxy/tree-table/component/tree-table.component.ts b/components/automate-ui/src/app/modules/infra-proxy/tree-table/component/tree-table.component.ts
index 1071fb901..37c02b506 100644
--- a/components/automate-ui/src/app/modules/infra-proxy/tree-table/component/tree-table.component.ts
+++ b/components/automate-ui/src/app/modules/infra-proxy/tree-table/component/tree-table.component.ts
@@ -52,6 +52,7 @@ export class TreeTableComponent<T> implements OnInit, OnChanges {
     this.searchableTree = this.tree.map(t => this.converterService.toSearchableTree(t));
     const treeTableTree = this.searchableTree.map(st => this.converterService.toTreeTableTree(st));
     this.treeTable = flatMap(treeTableTree, this.treeService.flatten);
+    this.treeCollapsed();
     this.dataSource = this.generateDataSource();
   }
 
@@ -63,6 +64,7 @@ export class TreeTableComponent<T> implements OnInit, OnChanges {
     this.searchableTree = this.tree.map(t => this.converterService.toSearchableTree(t));
     const treeTableTree = this.searchableTree.map(st => this.converterService.toTreeTableTree(st));
     this.treeTable = flatMap(treeTableTree, this.treeService.flatten);
+    this.treeCollapsed();
     this.dataSource = this.generateDataSource();
   }
 
@@ -88,6 +90,15 @@ export class TreeTableComponent<T> implements OnInit, OnChanges {
     this.nodeClicked.next(clickedNode);
   }
 
+  treeCollapsed() {
+    this.treeTable.forEach((item, index) => {
+      item.isExpanded = false;
+      if (item.depth > 0 && index > 0) {
+        item.isVisible = false;
+      }
+    });
+  }
+
   // Overrides default options with those specified by the user
   parseOptions(defaultOpts: Options<T>): Options<T> {
     return defaults(this.options, defaultOpts);
diff --git a/components/automate-ui/src/app/modules/token/token-list/api-token-list.component.scss b/components/automate-ui/src/app/modules/token/token-list/api-token-list.component.scss
index 1f4ad49e3..e2212f44f 100644
--- a/components/automate-ui/src/app/modules/token/token-list/api-token-list.component.scss
+++ b/components/automate-ui/src/app/modules/token/token-list/api-token-list.component.scss
@@ -9,3 +9,14 @@
     margin-top: 42px;
   }
 }
+
+::ng-deep .mat-select-panel {
+  position: relative;
+  margin-top: 12px;
+  right: 24px;
+  width: 54px;
+}
+
+::ng-deep .mat-select-panel-wrap {
+  margin-left: -75px;
+}
diff --git a/components/automate-ui/src/app/ngrx.effects.ts b/components/automate-ui/src/app/ngrx.effects.ts
index 5bce2c0f5..69f0cab1d 100644
--- a/components/automate-ui/src/app/ngrx.effects.ts
+++ b/components/automate-ui/src/app/ngrx.effects.ts
@@ -11,6 +11,7 @@ import { CredentialsEffects } from './pages/+compliance/+credentials/credentials
 // CredentialEffect is for the credential entities. Don't confuse it with CredentialsEffects.
 // CredentialsEffects will be removed when the credentials page is refactored.
 import { CredentialEffects } from './entities/credentials/credential.effects';
+import { DataBagsEffects } from './entities/data-bags/data-bags.effects';
 import { DesktopEffects } from './entities/desktop/desktop.effects';
 import { DestinationEffects } from './entities/destinations/destination.effects';
 import { EnvironmentEffects } from './entities/environments/environment.effects';
@@ -46,6 +47,7 @@ import { UserPermEffects } from './entities/userperms/userperms.effects';
       CookbookVersionsEffects,
       CredentialsEffects,
       CredentialEffects,
+      DataBagsEffects,
       DesktopEffects,
       DestinationEffects,
       EnvironmentEffects,
diff --git a/components/automate-ui/src/app/ngrx.reducers.ts b/components/automate-ui/src/app/ngrx.reducers.ts
index 8c59e18d6..b59dc6e6e 100644
--- a/components/automate-ui/src/app/ngrx.reducers.ts
+++ b/components/automate-ui/src/app/ngrx.reducers.ts
@@ -12,6 +12,7 @@ import * as clientRuns from './entities/client-runs/client-runs.reducer';
 import * as cookbookEntity from './entities/cookbooks/cookbook.reducer';
 import * as cookbookDetailsEntity from './entities/cookbooks/cookbook-details.reducer';
 import * as cookbookVersionsEntity from './entities/cookbooks/cookbook-versions.reducer';
+import * as dataBagsEntity from './entities/data-bags/data-bags.reducer';
 import * as desktopEntity from './entities/desktop/desktop.reducer';
 import * as environmentEntity from './entities/environments/environment.reducer';
 import * as infraRoleEntity from './entities/infra-roles/infra-role.reducer';
@@ -67,6 +68,7 @@ export interface NgrxStateAtom {
   cookbooks: cookbookEntity.CookbookEntityState;
   cookbookDetails: cookbookDetailsEntity.CookbookDetailsEntityState;
   cookbookVersions: cookbookVersionsEntity.CookbookVersionsEntityState;
+  dataBags: dataBagsEntity.DataBagsEntityState;
   destinations: destinationEntity.DestinationEntityState;
   environments: environmentEntity.EnvironmentEntityState;
   infraRoles: infraRoleEntity.InfraRoleEntityState;
@@ -182,6 +184,7 @@ export const defaultInitialState = {
   cookbooks: cookbookEntity.CookbookEntityInitialState,
   cookbookDetails: cookbookDetailsEntity.CookbookDetailsEntityInitialState,
   cookbookVersions: cookbookVersionsEntity.CookbookVersionsEntityInitialState,
+  dataBags: dataBagsEntity.DataBagsEntityInitialState,
   destinations: destinationEntity.DestinationEntityInitialState,
   environments: environmentEntity.EnvironmentEntityInitialState,
   infraRoles: infraRoleEntity.InfraRoleEntityInitialState,
@@ -231,6 +234,7 @@ export const ngrxReducers = {
   cookbookDetails: cookbookDetailsEntity.cookbookDetailsEntityReducer,
   cookbookVersions: cookbookVersionsEntity.cookbookVersionsEntityReducer,
   credentialEntity: credential.credentialReducer,
+  dataBags: dataBagsEntity.dataBagsEntityReducer,
   destinations: destinationEntity.destinationEntityReducer,
   environments: environmentEntity.environmentEntityReducer,
   infraRoles: infraRoleEntity.infraRoleEntityReducer,
diff --git a/components/automate-ui/src/app/page-components/create-user-modal/create-user-modal.component.ts b/components/automate-ui/src/app/page-components/create-user-modal/create-user-modal.component.ts
index 3772b938f..6f5b46b78 100644
--- a/components/automate-ui/src/app/page-components/create-user-modal/create-user-modal.component.ts
+++ b/components/automate-ui/src/app/page-components/create-user-modal/create-user-modal.component.ts
@@ -60,6 +60,7 @@ export class CreateUserModalComponent implements OnInit, OnDestroy {
         this.passwordError = false;
         this.createUserForm.reset();
         this.visible = true;
+        this.modifyUsername = false;
       });
 
     this.store.select(createStatus).pipe(
@@ -94,7 +95,6 @@ export class CreateUserModalComponent implements OnInit, OnDestroy {
   }
 
   closeCreateModal(): void {
-    this.modifyUsername = false;
     this.visible = false;
   }
 
diff --git a/components/automate-ui/src/app/page-components/navbar/navbar.component.scss b/components/automate-ui/src/app/page-components/navbar/navbar.component.scss
index 5eac7f2f1..4e199a629 100644
--- a/components/automate-ui/src/app/page-components/navbar/navbar.component.scss
+++ b/components/automate-ui/src/app/page-components/navbar/navbar.component.scss
@@ -46,7 +46,7 @@ header {
       height: ($navigation-height / 2) - 2px;
       line-height: ($navigation-height / 2) - 2px;
       border-top: 1px solid $chef-grey;
-      overflow-x: auto;
+      overflow: auto hidden;
     }
 
     app-authorized {
diff --git a/components/automate-ui/src/app/pages/+compliance/+reporting/+reporting-nodes/reporting-nodes.component.html b/components/automate-ui/src/app/pages/+compliance/+reporting/+reporting-nodes/reporting-nodes.component.html
index afcd0bf2e..9a22d83ef 100644
--- a/components/automate-ui/src/app/pages/+compliance/+reporting/+reporting-nodes/reporting-nodes.component.html
+++ b/components/automate-ui/src/app/pages/+compliance/+reporting/+reporting-nodes/reporting-nodes.component.html
@@ -10,7 +10,7 @@
   <chef-phat-radio
     class="nodes-list-status-filters"
     [value]="nodeFilterStatus"
-    (change)="filterNodeStatus($event, $event.target.value)">
+    (change)="statusFilter($event, $event.target.value)">
     <chef-option class="filter all" value='all'>
       <span class="filter-label">Total Nodes</span>
       <span class="filter-total">
@@ -85,7 +85,7 @@
       </chef-tr>
     </chef-thead>
     <chef-tbody *ngIf="!reportData.nodesListLoading">
-      <chef-tr *ngFor="let node of filteredNodes(reportData.nodesList.items)">
+      <chef-tr *ngFor="let node of reportData.nodesList.items">
         <chef-td>
           <chef-icon *ngIf="node.latest_report.status !== 'waived'" class="status-icon" [ngClass]="node.latest_report.status">
             {{ statusIcon(node.latest_report.status) }}
diff --git a/components/automate-ui/src/app/pages/+compliance/+reporting/+reporting-nodes/reporting-nodes.component.ts b/components/automate-ui/src/app/pages/+compliance/+reporting/+reporting-nodes/reporting-nodes.component.ts
index 017dfa0f7..b46533377 100644
--- a/components/automate-ui/src/app/pages/+compliance/+reporting/+reporting-nodes/reporting-nodes.component.ts
+++ b/components/automate-ui/src/app/pages/+compliance/+reporting/+reporting-nodes/reporting-nodes.component.ts
@@ -2,9 +2,11 @@ import { takeUntil } from 'rxjs/operators';
 import { Component, OnInit, OnDestroy } from '@angular/core';
 import { Subject } from 'rxjs';
 import { groupBy } from 'lodash';
+import { includes } from 'lodash/fp';
 import * as moment from 'moment/moment';
 import { FilterC } from '../types';
 import { paginationOverride } from '../shared';
+import { TelemetryService } from 'app/services/telemetry/telemetry.service';
 import {
   StatsService,
   ReportQueryService,
@@ -25,10 +27,9 @@ export class ReportingNodesComponent implements OnInit, OnDestroy {
   layerTwoData: any = {};
   control: any = {};
   nodeFilterStatus = 'all';
-
   scanResultsPane = 0;
   openControls = {};
-
+  private allowedStatus = ['failed', 'passed', 'skipped', 'waived'];
   private isDestroyed: Subject<boolean> = new Subject<boolean>();
 
   constructor(
@@ -36,10 +37,12 @@ export class ReportingNodesComponent implements OnInit, OnDestroy {
     public reportQuery: ReportQueryService,
     public reportData: ReportDataService,
     private router: Router,
-    private route: ActivatedRoute
+    private route: ActivatedRoute,
+    private telemetryService: TelemetryService
   ) {}
 
   ngOnInit() {
+    this.nodeFilterStatus = this.route.queryParams['_value'].status || 'all';
     this.reportQuery.state.pipe(
       takeUntil(this.isDestroyed))
       .subscribe(this.getData.bind(this));
@@ -50,20 +53,27 @@ export class ReportingNodesComponent implements OnInit, OnDestroy {
     this.isDestroyed.complete();
   }
 
-  filterNodeStatus(_event, status) {
+  public statusFilter(_event, status: string): void {
     this.nodeFilterStatus = status;
+
+    const queryParams = {...this.route.snapshot.queryParams};
+
+    delete queryParams['status'];
+    delete queryParams['page'];
+
+    if ( includes(status, this.allowedStatus) ) {
+      queryParams['status'] = status;
+      this.telemetryService.track('applicationsStatusFilter',
+        { entity: 'reportingNodes', statusFilter: status});
+    }
+
+    this.router.navigate([], {queryParams});
   }
 
   isNodeStatusSelected(status): boolean {
     return this.nodeFilterStatus === status;
   }
 
-  filteredNodes(nodes) {
-    return this.nodeFilterStatus === 'all'
-      ? nodes
-      : nodes.filter((node) => node.latest_report.status === this.nodeFilterStatus);
-  }
-
   onNodesListPageChanged(event) {
     const reportQuery = this.reportQuery.getReportQuery();
     this.reportData.nodesListParams.page = event;
diff --git a/components/automate-ui/src/app/pages/+compliance/shared/reporting/report-data.service.spec.ts b/components/automate-ui/src/app/pages/+compliance/shared/reporting/report-data.service.spec.ts
index bd7392d0a..283c49625 100644
--- a/components/automate-ui/src/app/pages/+compliance/shared/reporting/report-data.service.spec.ts
+++ b/components/automate-ui/src/app/pages/+compliance/shared/reporting/report-data.service.spec.ts
@@ -1,4 +1,5 @@
 import { TestBed } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
 import { HttpClientTestingModule } from '@angular/common/http/testing';
 import { of as observableOf } from 'rxjs';
 import { StatsService } from './stats.service';
@@ -23,6 +24,7 @@ describe('ReportDataService', () => {
         { provide: TelemetryService, useClass: MockTelemetryService }
       ],
       imports: [
+        RouterTestingModule,
         HttpClientTestingModule
       ]
     });
diff --git a/components/automate-ui/src/app/pages/+compliance/shared/reporting/stats.service.spec.ts b/components/automate-ui/src/app/pages/+compliance/shared/reporting/stats.service.spec.ts
index 7a93af6a0..8fc1a1543 100644
--- a/components/automate-ui/src/app/pages/+compliance/shared/reporting/stats.service.spec.ts
+++ b/components/automate-ui/src/app/pages/+compliance/shared/reporting/stats.service.spec.ts
@@ -1,4 +1,5 @@
 import { TestBed } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
 import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
 import { ChefSessionService } from 'app/services/chef-session/chef-session.service';
 import { MockChefSessionService } from 'app/testing/mock-chef-session.service';
@@ -12,7 +13,6 @@ const COMPLIANCE_URL = environment.compliance_url;
 describe('StatsService', () => {
   let httpTestingController: HttpTestingController;
   let service: StatsService;
-
   beforeEach(() => {
     TestBed.configureTestingModule({
       providers: [
@@ -20,6 +20,7 @@ describe('StatsService', () => {
         StatsService
       ],
       imports: [
+        RouterTestingModule,
         HttpClientTestingModule
       ]
     });
diff --git a/components/automate-ui/src/app/pages/+compliance/shared/reporting/stats.service.ts b/components/automate-ui/src/app/pages/+compliance/shared/reporting/stats.service.ts
index 568b2dc8b..e2ac8bc18 100644
--- a/components/automate-ui/src/app/pages/+compliance/shared/reporting/stats.service.ts
+++ b/components/automate-ui/src/app/pages/+compliance/shared/reporting/stats.service.ts
@@ -2,6 +2,7 @@ import { map } from 'rxjs/operators';
 import { Injectable } from '@angular/core';
 import { HttpClient } from '@angular/common/http';
 import { Observable } from 'rxjs';
+import { ActivatedRoute } from '@angular/router';
 import * as moment from 'moment/moment';
 import { omitBy, isNil } from 'lodash';
 import { environment } from '../../../../../environments/environment';
@@ -20,7 +21,8 @@ export type ControlStatus = 'passed' | 'failed' | 'waived' | 'skipped';
 @Injectable()
 export class StatsService {
   constructor(
-    private httpClient: HttpClient
+    private httpClient: HttpClient,
+    private route: ActivatedRoute
   ) {}
 
   getFailures(types: Array<string>, reportQuery: ReportQuery): Observable<any> {
@@ -100,8 +102,9 @@ export class StatsService {
 
   getNodes(reportQuery: ReportQuery, listParams: any): Observable<any> {
     const url = `${CC_API_URL}/reporting/nodes/search`;
+    let formatted = this.formatFilters(reportQuery);
+    formatted = this.addStatusParam(formatted);
 
-    const formatted = this.formatFilters(reportQuery);
     let body = { filters: formatted };
 
     const {page, perPage} = listParams;
@@ -119,6 +122,14 @@ export class StatsService {
         ({ total, total_failed, total_passed, total_skipped, total_waived, items: nodes })));
   }
 
+  private addStatusParam(filters): any {
+    const statusValue = this.route.queryParams['_value'].status;
+    if (statusValue) {
+      filters.push({ type: 'status', values: [statusValue] });
+    }
+    return filters;
+  }
+
   getProfiles(reportQuery: ReportQuery, listParams: any): Observable<any> {
     const url = `${CC_API_URL}/reporting/profiles`;
 
diff --git a/components/automate-ui/src/app/pages/+compliance/shared/reporting/suggestions.service.spec.ts b/components/automate-ui/src/app/pages/+compliance/shared/reporting/suggestions.service.spec.ts
index 93943410d..185220b69 100644
--- a/components/automate-ui/src/app/pages/+compliance/shared/reporting/suggestions.service.spec.ts
+++ b/components/automate-ui/src/app/pages/+compliance/shared/reporting/suggestions.service.spec.ts
@@ -1,4 +1,5 @@
 import { TestBed } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
 import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
 import { CookieModule } from 'ngx-cookie';
 import { SuggestionsService } from './suggestions.service';
@@ -16,6 +17,7 @@ describe('SuggestionsService', () => {
   beforeEach(() => {
     TestBed.configureTestingModule({
       imports: [
+        RouterTestingModule,
         CookieModule.forRoot(),
         HttpClientTestingModule
       ],
diff --git a/components/automate-ui/src/app/pages/create-data-feed-modal/create-data-feed-modal.component.html b/components/automate-ui/src/app/pages/create-data-feed-modal/create-data-feed-modal.component.html
index 24557b3bd..8a35e0b0b 100644
--- a/components/automate-ui/src/app/pages/create-data-feed-modal/create-data-feed-modal.component.html
+++ b/components/automate-ui/src/app/pages/create-data-feed-modal/create-data-feed-modal.component.html
@@ -21,16 +21,13 @@
         <div class="margin">
           <chef-form-field>
             <label>
-              <span class="label">Data Feed URL<span aria-hidden="true">*</span></span>
+              <span class="label">Data Feed URL <span aria-hidden="true">*</span></span>
               <input chefInput formControlName="url" type="text" id="url-input" (keyup)="handleInput($event)" autocomplete="off"/>
             </label>
             <chef-error
-            *ngIf="createForm.get('url').hasError('required') && createForm.get('url').dirty">
+            *ngIf="(createForm.get('url').hasError('required') || createForm.get('url').hasError('pattern')) && createForm.get('url').dirty">
             Data Feed URL is required.
             </chef-error>
-            <chef-error *ngIf="createForm.get('url').hasError('pattern')">
-              Data Feed URL is invalid.
-            </chef-error>
           </chef-form-field>
         </div>
         <div class="margin">
@@ -68,7 +65,7 @@
             <chef-icon class="url-failed-icon">warning</chef-icon>
             <span>Unable to connect, check URL, username and password.</span>
           </div>
-          
+
           <div class="url-success" *ngIf="hookStatus === urlState.Success">
             <chef-icon class="url-success-icon">done</chef-icon>
             <span class="status-heading">Data feed test connected successfully!</span>
diff --git a/components/automate-ui/src/app/pages/data-feed-details/data-feed-details.component.html b/components/automate-ui/src/app/pages/data-feed-details/data-feed-details.component.html
index f4fa2d224..f583cb525 100644
--- a/components/automate-ui/src/app/pages/data-feed-details/data-feed-details.component.html
+++ b/components/automate-ui/src/app/pages/data-feed-details/data-feed-details.component.html
@@ -35,12 +35,9 @@
           <chef-form-field>
             <label>Data Feed URL <span aria-hidden="true">*</span></label>
             <input chefInput formControlName="url" type="text" [resetOrigin]="saveSuccessful" autocomplete="off" data-cy="url-input"/>
-            <chef-error *ngIf="urlCtrl.hasError('required')">
+            <chef-error *ngIf="(urlCtrl.hasError('required') || urlCtrl.hasError('pattern')) && urlCtrl.dirty">
               Data Feed URL is required.
             </chef-error>
-            <chef-error *ngIf="urlCtrl.hasError('pattern')">
-              Data Feed URL is invalid.
-            </chef-error>
           </chef-form-field>
         </form>
         <div>
diff --git a/components/automate-ui/src/app/pages/data-feed-details/data-feed-details.component.ts b/components/automate-ui/src/app/pages/data-feed-details/data-feed-details.component.ts
index 77a03c134..f664b0d61 100644
--- a/components/automate-ui/src/app/pages/data-feed-details/data-feed-details.component.ts
+++ b/components/automate-ui/src/app/pages/data-feed-details/data-feed-details.component.ts
@@ -72,10 +72,8 @@ export class DataFeedDetailsComponent implements OnInit, OnDestroy {
     this.updateForm = this.fb.group({
       // Must stay in sync with error checks in data-feed-details.component.html
       name: ['', [Validators.required, Validators.pattern(Regex.patterns.NON_BLANK)]],
-      url: ['', [Validators.required,
-        Validators.pattern(Regex.patterns.NON_BLANK),
-        Validators.pattern(Regex.patterns.VALID_FQDN)
-      ]]
+      // Note that URL here may be FQDN -or- IP!
+      url: ['', [Validators.required, Validators.pattern(Regex.patterns.NON_BLANK)]]
     });
 
     this.store.pipe(
diff --git a/components/automate-ui/src/app/pages/data-feed/data-feed.component.spec.ts b/components/automate-ui/src/app/pages/data-feed/data-feed.component.spec.ts
index 8ec1ae265..17bc8d313 100644
--- a/components/automate-ui/src/app/pages/data-feed/data-feed.component.spec.ts
+++ b/components/automate-ui/src/app/pages/data-feed/data-feed.component.spec.ts
@@ -177,40 +177,20 @@ describe('DataFeedComponent', () => {
       errors = url.errors || {};
       expect(errors['required']).toBeTruthy();
 
-      // Set url to invalid inputs
-      url.setValue('http://foo.bar-.-.');
-      errors = url.errors || {};
-      expect(errors['pattern']).toBeTruthy();
-
-      url.setValue('http://foo.bar..com/');
-      errors = url.errors || {};
-      expect(errors['pattern']).toBeTruthy();
-
-      url.setValue('http://...foo.bar.com/');
+      url.setValue('');
       errors = url.errors || {};
-      expect(errors['pattern']).toBeTruthy();
+      expect(errors['required']).toBeTruthy();
 
-      url.setValue('http://...foo.bar com/');
+      // Set url to invalid inputs
+      url.setValue('  ');
       errors = url.errors || {};
       expect(errors['pattern']).toBeTruthy();
+      expect(errors['required']).toBeFalsy();
 
       // Set url to valid inputs
-      url.setValue('http://fo_o.bar/');
+      url.setValue('any');
       errors = url.errors || {};
       expect(errors['pattern']).toBeFalsy();
-
-      url.setValue('https://bit.ly/2OWGwiL');
-      errors = url.errors || {};
-      expect(errors['pattern']).toBeFalsy();
-
-      url.setValue('http://demo.com/');
-      errors = url.errors || {};
-      expect(errors['pattern']).toBeFalsy();
-
-      url.setValue('https://example_test.co.in/2OWGwiL');
-      errors = url.errors || {};
-      expect(errors['pattern']).toBeFalsy();
-
     });
   });
 });
diff --git a/components/automate-ui/src/app/pages/data-feed/data-feed.component.ts b/components/automate-ui/src/app/pages/data-feed/data-feed.component.ts
index 8a8881c20..9279400ce 100644
--- a/components/automate-ui/src/app/pages/data-feed/data-feed.component.ts
+++ b/components/automate-ui/src/app/pages/data-feed/data-feed.component.ts
@@ -66,14 +66,10 @@ export class DataFeedComponent implements OnInit, OnDestroy {
     this.createDataFeedForm = this.fb.group({
       // Must stay in sync with error checks in create-data-feed-modal.component.html
       name: ['', [Validators.required, Validators.pattern(Regex.patterns.NON_BLANK)]],
-      url: ['', [Validators.required,
-        Validators.pattern(Regex.patterns.NON_BLANK),
-        Validators.pattern(Regex.patterns.VALID_FQDN)
-      ]],
+      // Note that URL here may be FQDN -or- IP!
+      url: ['', [Validators.required, Validators.pattern(Regex.patterns.NON_BLANK)]],
       username: ['', [Validators.required, Validators.pattern(Regex.patterns.NON_BLANK)]],
-      password: ['', [Validators.required,
-        Validators.pattern(Regex.patterns.NON_BLANK)
-      ]]
+      password: ['', [Validators.required, Validators.pattern(Regex.patterns.NON_BLANK)]]
     });
   }
 
diff --git a/components/automate-ui/src/app/pages/notifications/notifications.component.html b/components/automate-ui/src/app/pages/notifications/notifications.component.html
index 86b4ba564..6ec6d81ad 100644
--- a/components/automate-ui/src/app/pages/notifications/notifications.component.html
+++ b/components/automate-ui/src/app/pages/notifications/notifications.component.html
@@ -49,14 +49,21 @@
                 </chef-td>
                 <chef-td class="actions three-dot-column">
                   <mat-select panelClass="chef-control-menu">
-                    <mat-option (onSelectionChange)="deleteRule(rule)">Delete Notification</mat-option>
+                    <mat-option (onSelectionChange)="startNotificationDelete($event, rule)" data-cy="remove-notification">Delete Notification</mat-option>
                   </mat-select>
                 </chef-td>
               </chef-tr>
             </chef-tbody>
           </chef-table>
         </app-authorized>
-
+        <app-delete-object-modal
+          [visible]="deleteModalVisible"
+          objectNoun="notification"
+          [objectName]="notificationToDelete?.name"
+          (close)="closeDeleteModal()"
+          (deleteClicked)="deleteNotification()"
+          objectAction="Delete">
+        </app-delete-object-modal>
         <app-page-picker
           [total]="(rules$ | async)?.length"
           [perPage]="pageSize"
diff --git a/components/automate-ui/src/app/pages/notifications/notifications.component.spec.ts b/components/automate-ui/src/app/pages/notifications/notifications.component.spec.ts
index 7801ded90..de7e5e88a 100644
--- a/components/automate-ui/src/app/pages/notifications/notifications.component.spec.ts
+++ b/components/automate-ui/src/app/pages/notifications/notifications.component.spec.ts
@@ -1,7 +1,5 @@
 import { TestBed } from '@angular/core/testing';
 import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
-import { MatDialog } from '@angular/material/dialog';
-import { MatSnackBar } from '@angular/material/snack-bar';
 import { By } from '@angular/platform-browser';
 import { Observable, of as observableOf } from 'rxjs';
 import { Store, StoreModule } from '@ngrx/store';
@@ -41,22 +39,6 @@ describe('NotificationsComponent', () => {
     }
   }
 
-  class MockDeleteDialog {
-    afterClosed() {
-      return observableOf('delete');
-    }
-  }
-
-  class MockMdDialog {
-    open(_dialogType) {
-      return new MockDeleteDialog();
-    }
-  }
-
-  class MockMdSnackBar {
-    open(_message, _m, _time) { }
-  }
-
   let telemetryService: TelemetryService;
   let fixture, component;
 
@@ -70,10 +52,8 @@ describe('NotificationsComponent', () => {
         NotificationsComponent
       ],
       providers: [
-        { provide: MatDialog, useClass: MockMdDialog },
         { provide: TelemetryService, useClass: MockTelemetryService },
         { provide: RulesService, useClass: MockRulesService },
-        { provide: MatSnackBar, useClass: MockMdSnackBar},
         FeatureFlagsService
       ],
       schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
@@ -112,7 +92,7 @@ describe('NotificationsComponent', () => {
     });
 
     it('ensure telemetry is sent on deleting a rule', () => {
-      component.deleteRule(new Rule('', '', null, '', ServiceActionType.SLACK, '', false));
+      component.deleteNotification();
 
       expect(telemetryService.track).toHaveBeenCalled();
     });
diff --git a/components/automate-ui/src/app/pages/notifications/notifications.component.ts b/components/automate-ui/src/app/pages/notifications/notifications.component.ts
index 2915ee989..e00cae321 100644
--- a/components/automate-ui/src/app/pages/notifications/notifications.component.ts
+++ b/components/automate-ui/src/app/pages/notifications/notifications.component.ts
@@ -1,18 +1,13 @@
-import { of as observableOf,  Observable } from 'rxjs';
+import { Observable } from 'rxjs';
 
-import { map, filter } from 'rxjs/operators';
+import { map } from 'rxjs/operators';
 import { Component, OnInit } from '@angular/core';
-import { MatDialog } from '@angular/material/dialog';
-import { MatSnackBar } from '@angular/material/snack-bar';
-
+import { MatOptionSelectionChange } from '@angular/material/core/option';
 import { LayoutFacadeService, Sidebar } from 'app/entities/layout/layout.facade';
 import { Rule, ServiceActionType } from './rule';
 import { SortDirection } from '../../types/types';
 import { RulesService } from '../../services/rules/rules.service';
 import { TelemetryService } from '../../services/telemetry/telemetry.service';
-import {
-  DeleteNotificationDialogComponent
-} from 'app/page-components/delete-notification-dialog/delete-notification-dialog.component';
 
 export interface FieldDirection {
   node_name: SortDirection;
@@ -25,7 +20,7 @@ export interface FieldDirection {
   styleUrls: ['./notifications.component.scss']
 })
 export class NotificationsComponent implements OnInit {
-  rules$: Observable<Rule[]> = observableOf([]);
+  rules$: Observable<Rule[]>;
   errorLoading = false;
   currentPage = 1;
   pageSize = 10;
@@ -34,12 +29,12 @@ export class NotificationsComponent implements OnInit {
   permissionDenied = false; // not currently used
   // This is exposed here to allow the component HTML access to ServiceActionType
   serviceActionType = ServiceActionType;
+  public notificationToDelete: Rule;
+  public deleteModalVisible = false;
 
   constructor(
     private layoutFacade: LayoutFacadeService,
     private service: RulesService,
-    public dialog: MatDialog,
-    public snackBar: MatSnackBar,
     private telemetryService: TelemetryService
   ) { }
 
@@ -73,6 +68,7 @@ export class NotificationsComponent implements OnInit {
     this.sortField = field;
     this.updateSort(field, this.sortDir[field]);
   }
+
   sortIcon(field: string): string {
     if (field === this.sortField) {
       return 'sort-' + this.sortDir[field];
@@ -81,24 +77,22 @@ export class NotificationsComponent implements OnInit {
     }
   }
 
+  public startNotificationDelete($event: MatOptionSelectionChange, rule: Rule): void {
+    if ($event.isUserInput) {
+      this.notificationToDelete = rule;
+      this.deleteModalVisible = true;
+    }
+  }
 
-  deleteRule(rule: Rule) {
-    const dialogRef2 = this.dialog.open(DeleteNotificationDialogComponent);
-    dialogRef2.afterClosed().pipe(
-      filter((result: any) => result === 'delete' ))
-      .subscribe(_result => {
-        this.service.deleteRule(rule).subscribe(_res => {
-          this.snackBarMessage(`Rule '${rule.name}' was deleted.`);
-          this.refreshRules();
-        }, err => {
-          const body = err;
-          this.snackBarMessage(`Could not delete rule '${rule.name}': ${body}`);
-        });
-      });
+  public deleteNotification(): void {
+    this.closeDeleteModal();
+    this.service.deleteRule(this.notificationToDelete).subscribe(_res => {
+      this.refreshRules();
+    });
   }
 
-  snackBarMessage(message) {
-    this.snackBar.open(message, '', { duration: 6000 } );
+  public closeDeleteModal(): void {
+    this.deleteModalVisible = false;
   }
 
   private updateSort(field: string, direction: string) {
diff --git a/components/automate-ui/src/styles.scss b/components/automate-ui/src/styles.scss
index 427a1d55e..65c5660bc 100644
--- a/components/automate-ui/src/styles.scss
+++ b/components/automate-ui/src/styles.scss
@@ -342,6 +342,29 @@ input {
   top: -2px;
 }
 
+.chef-servers-icon {
+  display: inline-block;
+  border: 1px solid $chef-darkest;
+  border-radius: 2px;
+  height: 16px;
+  width: 16px;
+  background-color: var(--chef-white);
+  position: relative;
+  top: 2px;
+  margin-right: 5px;
+}
+
+.chef-servers-icon:before {
+  color: $chef-darkest;
+  content: 'C';
+  font-size: 9px;
+  font-weight: bolder;
+  position: absolute;
+  right: 4px;
+  text-align: center;
+  top: -3px;
+}
+
 // mat-select dropdown menu
 
 [panelClass="chef-dropdown"] {
diff --git a/components/compliance-service/cmd/compliance-service/cmd/run.go b/components/compliance-service/cmd/compliance-service/cmd/run.go
index af8426f3d..381218ea1 100644
--- a/components/compliance-service/cmd/compliance-service/cmd/run.go
+++ b/components/compliance-service/cmd/compliance-service/cmd/run.go
@@ -26,12 +26,14 @@ var conf = config.Compliance{
 		MigrationsPath:   "dao/pgdb/migration/sql/",
 	},
 	InspecAgent: config.InspecAgent{
-		JobBufferSize: 1000,
-		JobWorkers:    10,
-		BackendCache:  "true",
-		AuthnTarget:   "0.0.0.0:10113",
-		AutomateFQDN:  "localhost",
-		TmpDir:        os.Getenv("TMPDIR"),
+		JobBufferSize:       1000,
+		JobWorkers:          10,
+		BackendCache:        "true",
+		AuthnTarget:         "0.0.0.0:10113",
+		AutomateFQDN:        "localhost",
+		TmpDir:              os.Getenv("TMPDIR"),
+		ResultMessageLimit:  10000,
+		ControlResultsLimit: 50,
 	},
 	ElasticSearch: config.ElasticSearch{},
 	ElasticSearchSidecar: config.ElasticSearchSidecar{
@@ -120,6 +122,8 @@ func init() {
 	runCmd.Flags().StringVar(&conf.InspecAgent.AutomateFQDN, "automate-fqdn", conf.InspecAgent.AutomateFQDN, "Target fqdn for inspec reporting to automate")
 	runCmd.Flags().StringVar(&conf.InspecAgent.TmpDir, "inspec-tmp-dir", conf.InspecAgent.TmpDir, "location of /tmp dir to be used by inspec for caching")
 	runCmd.Flags().StringVar(&conf.InspecAgent.RemoteInspecVersion, "remote-inspec-version", conf.InspecAgent.RemoteInspecVersion, "Option to specify the version of inspec to use for remote(e.g. AWS SSM) scan jobs")
+	runCmd.Flags().IntVar(&conf.InspecAgent.ResultMessageLimit, "result-message-limit", conf.InspecAgent.ResultMessageLimit, "A control result message that exceeds this character limit will be truncated")
+	runCmd.Flags().IntVar(&conf.InspecAgent.ControlResultsLimit, "control-results-limit", conf.InspecAgent.ControlResultsLimit, "The array of results per control will be truncated at this limit to avoid large reports that cannot be processed")
 
 	// Legacy Automate Headers/User Info
 	runCmd.Flags().StringVar(&conf.Delivery.Enterprise, "delivery-ent", conf.Delivery.Enterprise, "Automate Enterprise")
diff --git a/components/compliance-service/compliance.go b/components/compliance-service/compliance.go
index 0c2ac87d3..1c1e11519 100644
--- a/components/compliance-service/compliance.go
+++ b/components/compliance-service/compliance.go
@@ -119,6 +119,10 @@ func initBits(ctx context.Context, conf *config.Compliance) (db *pgdb.DB, connFa
 	} else {
 		inspec.BackendCache = backendCacheBool
 	}
+
+	inspec.ResultMessageLimit = conf.InspecAgent.ResultMessageLimit
+	runner.ControlResultsLimit = conf.InspecAgent.ControlResultsLimit
+
 	inspec.TmpDir = conf.InspecAgent.TmpDir
 	// Let's have something sensible if the temp dir is not specified
 	if inspec.TmpDir == "" {
diff --git a/components/compliance-service/config/types.go b/components/compliance-service/config/types.go
index c56930e7f..8f6cf753f 100644
--- a/components/compliance-service/config/types.go
+++ b/components/compliance-service/config/types.go
@@ -81,6 +81,8 @@ type InspecAgent struct {
 	AutomateFQDN        string
 	TmpDir              string
 	RemoteInspecVersion string
+	ResultMessageLimit  int
+	ControlResultsLimit int
 }
 
 // Postgres specific options
diff --git a/components/compliance-service/habitat/plan.sh b/components/compliance-service/habitat/plan.sh
index d39e79a08..c81490dcf 100644
--- a/components/compliance-service/habitat/plan.sh
+++ b/components/compliance-service/habitat/plan.sh
@@ -29,7 +29,7 @@ pkg_binds_optional=(
   [authn-service]="port"
   [notifications-service]="port"
 )
-inspec_release="chef/inspec/4.18.111/20200506000342"
+inspec_release="chef/inspec/4.18.114/20200514030048"
 pkg_deps=(
   "${local_platform_tools_origin:-chef}/automate-platform-tools"
   "${inspec_release}"
diff --git a/components/compliance-service/inspec-agent/runner/runner.go b/components/compliance-service/inspec-agent/runner/runner.go
index 8a8788ba1..8105b9b77 100644
--- a/components/compliance-service/inspec-agent/runner/runner.go
+++ b/components/compliance-service/inspec-agent/runner/runner.go
@@ -17,7 +17,10 @@ import (
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 
+	"sort"
+
 	ingest_events_compliance_api "github.com/chef/automate/api/interservice/compliance/ingest/events/compliance"
+	ingest_inspec "github.com/chef/automate/api/interservice/compliance/ingest/events/inspec"
 	"github.com/chef/automate/api/interservice/compliance/ingest/ingest"
 	"github.com/chef/automate/api/interservice/compliance/jobs"
 	"github.com/chef/automate/components/compliance-service/ingest/ingestic/mappings"
@@ -31,6 +34,9 @@ import (
 
 var ListenPort int = 2133
 
+// ControlResultsLimit used for configuring inspec exec command, passed in via config flag
+var ControlResultsLimit int = 50
+
 var (
 	ScanJobWorkflowName = cereal.NewWorkflowName("scan-job-workflow")
 
@@ -426,7 +432,7 @@ func (t *InspecJobTask) Run(ctx context.Context, task cereal.Task) (interface{},
 	}
 
 	cleanupKeys(job.TargetConfig.KeyFiles)
-	logrus.Debugf("job %s finished", job.JobID)
+	logrus.Debugf("job '%s' finished", job.JobID)
 
 	if job.NodeStatus == types.StatusRunning {
 		job.NodeStatus = types.StatusFailed
@@ -450,7 +456,7 @@ func (t *InspecJobTask) handleCompletedJob(ctx context.Context, job types.Inspec
 			// reporter config when we assembled the script
 			err := t.scannerServer.UpdateResult(ctx, &job, nil, jobInfo.InspecErr, job.Reporter.ReportUUID)
 			if err != nil {
-				logrus.Errorf("error trying to update node %s (%s) during scan job %s with job results: %s", job.NodeName, job.NodeID, job.JobName, err.Error())
+				logrus.Errorf("error trying to update node '%s' (%s) during scan job '%s' with job results: %s", job.NodeName, job.NodeID, job.JobName, err.Error())
 			}
 		default:
 			return types.StatusFailed, errors.Errorf("unknown job type: %s", job.JobType)
@@ -460,27 +466,27 @@ func (t *InspecJobTask) handleCompletedJob(ctx context.Context, job types.Inspec
 		case types.JobTypeDetect:
 			detectInfoByte, err := json.Marshal(jobInfo.DetectInfo)
 			if err != nil {
-				logrus.Errorf("error trying to marshal detectInfo for job %s", job.JobID)
+				logrus.Errorf("error trying to marshal detectInfo for job '%s'", job.JobID)
 				job.NodeStatus = types.StatusFailed
 				jobInfo.InspecErr = inspec.NewInspecError(inspec.INVALID_OUTPUT, err.Error())
 			}
 			err = t.scannerServer.UpdateResult(ctx, &job, detectInfoByte, jobInfo.InspecErr, "")
 			if err != nil {
-				logrus.Errorf("error trying to update node %s (%s) during scan job %s with job results: %s", job.NodeName, job.NodeID, job.JobName, err.Error())
+				logrus.Errorf("error trying to update node '%s' (%s) during scan job '%s' with job results: %s", job.NodeName, job.NodeID, job.JobName, err.Error())
 			}
 		case types.JobTypeExec:
 			if job.NodeStatus == types.StatusCompleted {
 				jobInfo.ReportID = uuid.Must(uuid.NewV4()).String()
 				err := t.reportIt(ctx, &job, jobInfo.ExecInfo, jobInfo.ReportID)
 				if err != nil {
-					logrus.Errorf("worker error reporting on node %s (%s) during scan job %s: %s", job.NodeName, job.NodeID, job.JobName, err.Error())
+					logrus.Errorf("worker error reporting on node '%s' (%s) during scan job '%s': %s", job.NodeName, job.NodeID, job.JobName, err.Error())
 					job.NodeStatus = types.StatusFailed
 					jobInfo.InspecErr = inspec.NewInspecError(inspec.INVALID_OUTPUT, err.Error())
 				}
 			}
 			err := t.scannerServer.UpdateResult(ctx, &job, nil, jobInfo.InspecErr, jobInfo.ReportID)
 			if err != nil {
-				logrus.Errorf("error trying to update node %s (%s) during scan job %s with job results: %s", job.NodeName, job.NodeID, job.JobName, err.Error())
+				logrus.Errorf("error trying to update node '%s' (%s) during scan job '%s' with job results: %s", job.NodeName, job.NodeID, job.JobName, err.Error())
 			}
 		default:
 			return types.StatusFailed, errors.Errorf("unknown job type: %+v", job.JobType)
@@ -488,9 +494,9 @@ func (t *InspecJobTask) handleCompletedJob(ctx context.Context, job types.Inspec
 	}
 	err := t.scannerServer.UpdateNode(ctx, &job, jobInfo.DetectInfo)
 	if err != nil {
-		logrus.Errorf("error trying to update node %s (%s) during scan job %s: %s", job.NodeName, job.NodeID, job.JobName, err.Error())
+		logrus.Errorf("error trying to update node '%s' (%s) during scan job '%s': %s", job.NodeName, job.NodeID, job.JobName, err.Error())
 	}
-	logrus.Debugf("finished job %s (%s) with status %s", job.JobName, job.JobID, job.NodeStatus)
+	logrus.Debugf("finished job '%s' (%s) with status '%s'", job.JobName, job.JobID, job.NodeStatus)
 	return job.NodeStatus, nil
 }
 
@@ -524,6 +530,9 @@ func (t *InspecJobTask) reportIt(ctx context.Context, job *types.InspecJob, cont
 	report.Tags = job.Tags
 	logrus.Debugf("hand-over report to ingest service")
 
+	removeResults(reportID, report.Profiles, ControlResultsLimit)
+	// strip profile meta if missing from the backend
+
 	_, err := t.ingestClient.ProcessComplianceReport(ctx, &report)
 	if err != nil {
 		return errors.Wrap(err, "Report processing error")
@@ -747,6 +756,38 @@ func doExec(job *types.InspecJob) (jsonBytes []byte, err *inspec.Error) {
 	return jsonBytes, nil
 }
 
+func removeResults(reportId string, profiles []*ingest_inspec.Profile, limit int) {
+	for _, profile := range profiles {
+		if profile.Controls != nil {
+			for _, control := range profile.Controls {
+				if control.Results != nil {
+					if len(control.Results) > limit {
+						logrus.Debugf("Control '%s' for report '%s' has %d results and will be trimmed based on the value (%d) of 'control-results-limit'", control.Id, reportId, len(control.Results), limit)
+						// Sort results in this non-alphabetical order: failed, skipped, passed. This order is needed
+						// to ensure the overall status of the control is still calculated correctly in ingestion
+						sort.Slice(control.Results, func(i, j int) bool {
+							return control.Results[i].Status == "failed" || (control.Results[i].Status == "skipped" && control.Results[j].Status == "passed")
+						})
+						chopped := ingest_inspec.RemovedResultsCounts{}
+						for i := limit; i < len(control.Results); i++ {
+							switch control.Results[i].Status {
+							case "failed":
+								chopped.Failed += 1
+							case "skipped":
+								chopped.Skipped += 1
+							case "passed":
+								chopped.Passed += 1
+							}
+						}
+						control.RemovedResultsCounts = &chopped
+						control.Results = control.Results[0:limit]
+					}
+				}
+			}
+		}
+	}
+}
+
 func potentialTargetConfigs(job *types.InspecJob) []inspec.TargetConfig {
 	// GCP profile requires the project_id to be passed in as an attribute. Used the SubscriptionId to get this value from the database
 	if job.TargetConfig.Backend == "gcp" {
diff --git a/components/compliance-service/inspec/cli.go b/components/compliance-service/inspec/cli.go
index e9241836a..b56515636 100644
--- a/components/compliance-service/inspec/cli.go
+++ b/components/compliance-service/inspec/cli.go
@@ -17,6 +17,9 @@ import (
 // BackendCache used for configuring inspec exec command, passed in via config flag
 var BackendCache bool
 
+// ResultMessageLimit used for configuring inspec exec command, passed in via config flag
+var ResultMessageLimit int
+
 // TmpDir is used for setting the location of the /tmp dir to be used by inspec for caching
 var TmpDir string
 
@@ -58,6 +61,8 @@ func Scan(paths []string, target *TargetConfig, timeout time.Duration, env map[s
 	// Using "json-automate" as it provides better inherited profiles report
 	target.Reporter["json-automate"] = Reporter{}
 	target.BackendCache = BackendCache
+	target.ResultIncludeBacktrace = false
+	target.ResultMessageLimit = ResultMessageLimit
 
 	env["HOME"] = os.Getenv("HOME")
 
@@ -81,9 +86,9 @@ func Scan(paths []string, target *TargetConfig, timeout time.Duration, env map[s
 		// ensuring that stdout ends with json character }
 		if stdOut[len(stdOut)-1] != '}' {
 			if len(stdOut) >= 80 {
-				stdOutErr += fmt.Sprintf("Expected InSpec STDOUT to end with '}' but found %q. Last 80 chars of STDOUT: %s\n", stdOut[len(stdOut)-1], stdOut[len(stdOut)-80:])
+				stdOutErr += fmt.Sprintf("Expected InSpec STDOUT to end with '}' but found %q. Last 80 chars of STDOUT: `%s`\n", stdOut[len(stdOut)-1], stdOut[len(stdOut)-80:])
 			} else {
-				stdOutErr += fmt.Sprintf("Expected InSpec STDOUT to end with '}' but found %q. STDOUT: %s\n", stdOut[len(stdOut)-1], stdOut)
+				stdOutErr += fmt.Sprintf("Expected InSpec STDOUT to end with '}' but found %q. STDOUT: `%s`\n", stdOut[len(stdOut)-1], stdOut)
 			}
 		}
 	}
@@ -299,6 +304,10 @@ func getInspecError(stdOut string, stdErr string, err error, target *TargetConfi
 		}
 	}
 
+	if err == nil {
+		err = errors.New("unknown error")
+	}
+
 	if err.Error() == "execute timeout" {
 		return NewInspecError(CONN_TIMEOUT,
 			"Failed to connect to "+target.Hostname+
diff --git a/components/compliance-service/inspec/types.go b/components/compliance-service/inspec/types.go
index 62e93b7e0..167514d11 100644
--- a/components/compliance-service/inspec/types.go
+++ b/components/compliance-service/inspec/types.go
@@ -257,19 +257,20 @@ type Secrets struct {
 }
 
 type TargetBaseConfig struct {
-	Backend        string              `json:"backend,omitempty"`
-	Hostname       string              `json:"host,omitempty"`
-	Port           int                 `json:"port,omitempty"`
-	LoginPath      string              `json:"login_path,omitempty"` // winrm
-	Sudo           bool                `json:"sudo,omitempty"`
-	Format         string              `json:"format,omitempty"`
-	Reporter       map[string]Reporter `json:"reporter,omitempty"`
-	Ssl            bool                `json:"ssl,omitempty"`
-	SslSelfSigned  bool                `json:"self_signed,omitempty"`
-	BackendCache   bool                `json:"backend_cache,omitempty"`
-	Region         string              `json:"region,omitempty"`
-	SubscriptionId string              `json:"subscription_id,omitempty"`
-	AttributesJson *json.RawMessage    `json:"attributes,omitempty"`
+	Backend                string              `json:"backend,omitempty"`
+	Hostname               string              `json:"host,omitempty"`
+	Port                   int                 `json:"port,omitempty"`
+	LoginPath              string              `json:"login_path,omitempty"` // winrm
+	Sudo                   bool                `json:"sudo,omitempty"`
+	Reporter               map[string]Reporter `json:"reporter,omitempty"`
+	Ssl                    bool                `json:"ssl,omitempty"`
+	SslSelfSigned          bool                `json:"self_signed,omitempty"`
+	BackendCache           bool                `json:"backend_cache,omitempty"`
+	Region                 string              `json:"region,omitempty"`
+	SubscriptionId         string              `json:"subscription_id,omitempty"`
+	AttributesJson         *json.RawMessage    `json:"attributes,omitempty"`
+	ResultIncludeBacktrace bool                `json:"attributes,reporter_backtrace_inclusion"`
+	ResultMessageLimit     int                 `json:"attributes,reporter_message_truncation"`
 }
 
 // TargetConfig is inspec's JSON config options
diff --git a/components/compliance-service/scripts/grpcurl_it.sh b/components/compliance-service/scripts/grpcurl_it.sh
new file mode 100755
index 000000000..22addabe3
--- /dev/null
+++ b/components/compliance-service/scripts/grpcurl_it.sh
@@ -0,0 +1,64 @@
+#!/bin/bash -e
+
+which -s grpcurl || (echo "grpcurl is not installed, aborting..." && exit 1)
+which -s jq || (echo "js is not installed, aborting..." && exit 2)
+
+COMP_PORT=${PORT:-10121}
+MANAGER_PORT=${MANAGER_PORT:-10120}
+HOST=${HOST:-localhost}
+SSL_CERT="$(PWD)/../../../dev/certs/compliance-service.crt"
+SSL_KEY="$(PWD)/../../../dev/certs/compliance-service.key"
+CURL_COMP="--insecure -cert $SSL_CERT -key $SSL_KEY $HOST:$COMP_PORT"
+CURL_OPTS_COMP="--insecure -cert $SSL_CERT -key $SSL_KEY $HOST:$COMP_PORT chef.automate.domain.compliance"
+CURL_OPTS_MANAGER="--insecure -cert $SSL_CERT -key $SSL_KEY $HOST:$MANAGER_PORT chef.automate.domain.nodemanager"
+RAND_NR=$((RANDOM%100))
+
+# grpcurl $CURL_COMP list
+# grpcurl $CURL_COMP describe chef.automate.domain.compliance.jobs.JobsService
+# grpcurl $CURL_COMP describe chef.automate.domain.compliance.jobs.Job
+
+echo && echo "Compliance Service version:"
+grpcurl $CURL_OPTS_COMP.version.VersionService/Version
+
+NODE_NAME="curl_it docker node $RAND_NR"
+echo && echo "Adding docker node '$NODE_NAME'"
+NODE_ID=`grpcurl -d @ $CURL_OPTS_MANAGER.nodes.NodesService/Create <<EOM | jq -r .id
+{
+  "name": "$NODE_NAME",
+  "manager": "automate",
+  "target_config": {
+    "backend": "docker",
+    "host": "cc_pg",
+    "sudo": false
+  }
+}
+EOM
+`
+
+echo && echo "Adding a detect job for the new node '$NODE_NAME' with id '$NODE_ID'"
+grpcurl -d @ $CURL_OPTS_COMP.jobs.JobsService/Create <<EOM
+{
+  "name": "curl_it docker node detect job $RAND_NR",
+  "type": "detect",
+  "nodes": ["$NODE_ID"],
+  "retries": 1
+}
+EOM
+
+echo && echo "Adding an exec job for the new node '$NODE_NAME' with id '$NODE_ID'"
+grpcurl -d @ $CURL_OPTS_COMP.jobs.JobsService/Create <<EOM
+{
+  "name": "curl_it docker node exec job $RAND_NR",
+  "type": "exec",
+  "nodes": ["$NODE_ID"],
+  "profiles": ["https://github.com/dev-sec/apache-baseline/archive/master.tar.gz"],
+  "retries": 1
+}
+EOM
+
+
+# List all nodes
+# grpcurl $CURL_OPTS_MANAGER.nodes.NodesService/List
+
+# List all jobs
+# grpcurl $CURL_OPTS_COMP.jobs.JobsService/List


Test patch:
diff --git a/api/config/dex/config_request_test.go b/api/config/dex/config_request_test.go
index 3962a31ff..261ec40c8 100644
--- a/api/config/dex/config_request_test.go
+++ b/api/config/dex/config_request_test.go
@@ -53,6 +53,7 @@ func TestValidate(t *testing.T) {
 			UsernameAttr:       w.String("username"),
 			GroupsAttr:         w.String("groups"),
 			NameIdPolicyFormat: w.String("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"),
+			AllowedGroups:      []string{"admins", "ops"},
 		}
 	}
 
@@ -369,6 +370,23 @@ func TestValidate(t *testing.T) {
 		})
 	})
 
+	t.Run("allowed_groups (saml)", func(t *testing.T) {
+		valid := map[string][]string{
+			"one group":  {"admins"},
+			"two groups": {"admins", "ops"},
+			"no groups":  {}, // should this warn?
+		}
+		for k, v := range valid {
+			t.Run(k, func(t *testing.T) {
+				cfg := dex.DefaultConfigRequest()
+				saml := completeSAML()
+				saml.AllowedGroups = v
+				cfg.V1.Sys.Connectors = &dex.ConfigRequest_V1_Connectors{Saml: saml}
+				assert.NoError(t, cfg.Validate())
+			})
+		}
+	})
+
 	t.Run("ca_contents", func(t *testing.T) {
 		tests := map[string]func(certs string) *dex.ConfigRequest_V1_Connectors{
 			"saml": func(certs string) *dex.ConfigRequest_V1_Connectors {
diff --git a/api/config/shared/config_test.go b/api/config/shared/config_test.go
index 9ab5ac3dd..ab33e4e6c 100644
--- a/api/config/shared/config_test.go
+++ b/api/config/shared/config_test.go
@@ -1,21 +1,60 @@
-package shared
+package shared_test
 
 import (
 	"testing"
 
+	"github.com/chef/automate/api/config/dex"
+	"github.com/chef/automate/api/config/shared"
+	"github.com/chef/toml"
 	"github.com/stretchr/testify/assert"
 )
 
 func TestAddKeyMissing(t *testing.T) {
-	c := NewInvalidConfigError()
+	c := shared.NewInvalidConfigError()
 	c.AddMissingKey("foo")
 
 	assert.EqualValues(t, c.MissingKeys(), []string{"foo"})
 }
 
 func TestAddInvalidValue(t *testing.T) {
-	c := NewInvalidConfigError()
+	c := shared.NewInvalidConfigError()
 	c.AddInvalidValue("foo", "bar")
 
 	assert.EqualValues(t, c.InvalidValues(), map[string]string{"foo": "bar"})
 }
+
+func TestDexSAMLAllowedGroupsOptionalList(t *testing.T) {
+	t.Run("no list", func(t *testing.T) {
+		c := loadDexSAMLConfigFromToml(`
+groups_attr = "groups"
+`)
+		setting := c.GetAllowedGroups()
+		assert.Equal(t, "groups", c.GetGroupsAttr().Value)
+		assert.Equal(t, []string(nil), setting) // not set means nil
+	})
+
+	t.Run("empty list", func(t *testing.T) {
+		c := loadDexSAMLConfigFromToml(`
+allowed_groups = []
+`)
+		setting := c.GetAllowedGroups()
+		assert.EqualValues(t, []string{}, setting) // set to empty list means []string{}
+	})
+
+	t.Run("non-empty list", func(t *testing.T) {
+		c := loadDexSAMLConfigFromToml(`
+allowed_groups = ["alices", "bobs"]
+`)
+		setting := c.GetAllowedGroups()
+		assert.EqualValues(t, []string{"alices", "bobs"}, setting)
+	})
+}
+
+func loadDexSAMLConfigFromToml(s string) *dex.ConfigRequest_V1_Saml {
+	c := &dex.ConfigRequest_V1_Saml{}
+	err := toml.Unmarshal([]byte(s), c)
+	if err != nil {
+		panic(err)
+	}
+	return c
+}
diff --git a/components/compliance-service/inspec-agent/runner/runner_test.go b/components/compliance-service/inspec-agent/runner/runner_test.go
index ad3be77e3..5e7c9b737 100644
--- a/components/compliance-service/inspec-agent/runner/runner_test.go
+++ b/components/compliance-service/inspec-agent/runner/runner_test.go
@@ -3,6 +3,7 @@ package runner
 import (
 	"testing"
 
+	ingest_inspec "github.com/chef/automate/api/interservice/compliance/ingest/events/inspec"
 	"github.com/chef/automate/components/compliance-service/inspec"
 	"github.com/chef/automate/components/compliance-service/inspec-agent/types"
 	"github.com/stretchr/testify/assert"
@@ -63,3 +64,136 @@ func TestPotentialTargetConfigs(t *testing.T) {
 		},
 	}, targetConfigs[1])
 }
+
+func TestRemoveResults(t *testing.T) {
+	profiles := []*ingest_inspec.Profile{
+		{
+			Title: "Test Profile 1",
+			Controls: []*ingest_inspec.Control{
+				{
+					Id: "p1c1",
+					Results: []*ingest_inspec.Result{
+						{
+							Message: "Resource is as expected",
+							Status:  "passed",
+						},
+						{Status: "passed"},
+						{Status: "failed"},
+						{Status: "skipped"},
+						{Status: "failed"},
+						{Status: "skipped"},
+						{Status: "skipped"},
+						{Status: "failed"},
+						{Status: "passed"},
+						{Status: "skipped"},
+						{Status: "skipped"},
+						{Status: "failed"},
+						{Status: "passed"},
+						{Status: "failed"},
+						{Status: "skipped"},
+						{Status: "failed"},
+						{Status: "failed"},
+						{Status: "skipped"},
+						{Status: "failed"},
+						{Status: "passed"},
+						{Status: "passed"},
+						{Status: "passed"},
+						{Status: "passed"},
+						{Status: "failed"},
+						{Status: "failed"},
+						{Status: "failed"},
+						{Status: "failed"},
+						{Status: "skipped"},
+						{Status: "skipped"},
+						{Status: "skipped"},
+						{Status: "skipped"},
+					},
+				},
+				{
+					Id: "p1c2",
+				},
+			},
+		},
+		{
+			Title: "Test Profile 2",
+		},
+	}
+
+	removeResults("test-report-id", profiles, 25)
+	assert.Equal(t, []*ingest_inspec.Profile{
+		{
+			Title: "Test Profile 1",
+			Controls: []*ingest_inspec.Control{
+				{
+					Id: "p1c1",
+					Results: []*ingest_inspec.Result{
+						{Status: "failed"},
+						{Status: "failed"},
+						{Status: "failed"},
+						{Status: "failed"},
+						{Status: "failed"},
+						{Status: "failed"},
+						{Status: "failed"},
+						{Status: "failed"},
+						{Status: "failed"},
+						{Status: "failed"},
+						{Status: "failed"},
+						{Status: "failed"},
+						{Status: "skipped"},
+						{Status: "skipped"},
+						{Status: "skipped"},
+						{Status: "skipped"},
+						{Status: "skipped"},
+						{Status: "skipped"},
+						{Status: "skipped"},
+						{Status: "skipped"},
+						{Status: "skipped"},
+						{Status: "skipped"},
+						{Status: "skipped"},
+						{Status: "passed"},
+						{Status: "passed"},
+					},
+					RemovedResultsCounts: &ingest_inspec.RemovedResultsCounts{
+						Passed: 6,
+					},
+				},
+				{
+					Id: "p1c2",
+				},
+			},
+		},
+		{
+			Title: "Test Profile 2",
+		},
+	}, profiles)
+
+	removeResults("test-report-id", profiles, 4)
+	assert.Equal(t, []*ingest_inspec.Profile{
+		{
+			Title: "Test Profile 1",
+			Controls: []*ingest_inspec.Control{
+				{
+					Id: "p1c1",
+					Results: []*ingest_inspec.Result{
+						{Status: "failed"},
+						{Status: "failed"},
+						{Status: "failed"},
+						{Status: "failed"},
+					},
+					RemovedResultsCounts: &ingest_inspec.RemovedResultsCounts{
+						Failed:  8,
+						Skipped: 11,
+						Passed:  2,
+					},
+				},
+				{
+					Id: "p1c2",
+				},
+			},
+		},
+		{
+			Title: "Test Profile 2",
+		},
+	}, profiles)
+
+}
diff --git a/integration/tests/a1_legacy_token.sh b/integration/tests/a1_legacy_token.sh
index 34b64ab93..2bfb4fbb8 100644
--- a/integration/tests/a1_legacy_token.sh
+++ b/integration/tests/a1_legacy_token.sh
@@ -39,10 +39,11 @@ EOF
 do_test_deploy() {
     # we want to ensure that the token can be used to ingest data
     local response_code
-    response_code=$(hab_curl -fsS -k -H "api-token: $token" "https://localhost/data-collector/v0" \
+    response_code=$(hab_curl -s -k -H "api-token: $token" "https://localhost/data-collector/v0" \
       -d '{}' -o /dev/null -w '%{response_code}')
     if ! grep -q 400 <<< "$response_code"; then
       log_error "unexpected response code \"$response_code\" (expected 400)"
       return 1
     fi
+    log_info "got expected response code (400)"
 }