Repository: clouditor/clouditor
Pull request #336 (2021-10-08 15:27:00)

Implementation of MEDINA metrics
This PR contains the implementation of several MEDINA metrics in our Repo policy language.


Patch:
diff --git a/api/assessment/assessment.pb.go b/api/assessment/assessment.pb.go
index 66cef055..14c86ae7 100644
--- a/api/assessment/assessment.pb.go
+++ b/api/assessment/assessment.pb.go
@@ -38,6 +38,8 @@ import (
 	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
 	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
 	emptypb "google.golang.org/protobuf/types/known/emptypb"
+	structpb "google.golang.org/protobuf/types/known/structpb"
+	timestamppb "google.golang.org/protobuf/types/known/timestamppb"
 	reflect "reflect"
 	sync "sync"
 )
@@ -275,15 +277,29 @@ func (x *AssessEvidenceResponse) GetStatus() bool {
 	return false
 }
 
-// A result resource, representing the result after assessing the cloud resource with id resource_id.
+// A result resource, representing the result after assessing the cloud resource
+// with id resource_id.
 type Result struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	ResourceId string `protobuf:"bytes,1,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"`
-	Compliant  bool   `protobuf:"varint,2,opt,name=compliant,proto3" json:"compliant,omitempty"`
-	MetricId   int32  `protobuf:"varint,3,opt,name=metric_id,json=metricId,proto3" json:"metric_id,omitempty"`
+	// Assessment result id
+	Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+	// Time of assessment
+	Timestamp *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+	// Reference to the metric the assessment was based on
+	MetricId string `protobuf:"bytes,3,opt,name=metric_id,json=metricId,proto3" json:"metric_id,omitempty"`
+	// Data corresponding to the metric by the given metric id
+	MetricData *MetricData `protobuf:"bytes,4,opt,name=metric_data,json=metricData,proto3" json:"metric_data,omitempty"`
+	// Compliant case: true or false
+	Compliant bool `protobuf:"varint,5,opt,name=compliant,proto3" json:"compliant,omitempty"`
+	// Reference to the assessed evidence
+	EvidenceId string `protobuf:"bytes,6,opt,name=evidence_id,json=evidenceId,proto3" json:"evidence_id,omitempty"`
+	// Reference to the resource of the assessed evidence
+	ResourceId string `protobuf:"bytes,7,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"`
+	// Some comments on the reason for non-compliance
+	NonComplianceComments string `protobuf:"bytes,8,opt,name=non_compliance_comments,json=nonComplianceComments,proto3" json:"non_compliance_comments,omitempty"`
 }
 
 func (x *Result) Reset() {
@@ -318,13 +334,34 @@ func (*Result) Descriptor() ([]byte, []int) {
 	return file_assessment_proto_rawDescGZIP(), []int{5}
 }
 
-func (x *Result) GetResourceId() string {
+func (x *Result) GetId() string {
 	if x != nil {
-		return x.ResourceId
+		return x.Id
 	}
 	return ""
 }
 
+func (x *Result) GetTimestamp() *timestamppb.Timestamp {
+	if x != nil {
+		return x.Timestamp
+	}
+	return nil
+}
+
+func (x *Result) GetMetricId() string {
+	if x != nil {
+		return x.MetricId
+	}
+	return ""
+}
+
+func (x *Result) GetMetricData() *MetricData {
+	if x != nil {
+		return x.MetricData
+	}
+	return nil
+}
+
 func (x *Result) GetCompliant() bool {
 	if x != nil {
 		return x.Compliant
@@ -332,11 +369,80 @@ func (x *Result) GetCompliant() bool {
 	return false
 }
 
-func (x *Result) GetMetricId() int32 {
+func (x *Result) GetEvidenceId() string {
 	if x != nil {
-		return x.MetricId
+		return x.EvidenceId
 	}
-	return 0
+	return ""
+}
+
+func (x *Result) GetResourceId() string {
+	if x != nil {
+		return x.ResourceId
+	}
+	return ""
+}
+
+func (x *Result) GetNonComplianceComments() string {
+	if x != nil {
+		return x.NonComplianceComments
+	}
+	return ""
+}
+
+type MetricData struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	TargetValue *structpb.Value `protobuf:"bytes,1,opt,name=target_value,json=targetValue,proto3" json:"target_value,omitempty"` // target value, e.g. string, number, array, ...
+	Operator    string          `protobuf:"bytes,2,opt,name=operator,proto3" json:"operator,omitempty"`                          // the operator, e.g. "=="
+}
+
+func (x *MetricData) Reset() {
+	*x = MetricData{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_assessment_proto_msgTypes[6]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *MetricData) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*MetricData) ProtoMessage() {}
+
+func (x *MetricData) ProtoReflect() protoreflect.Message {
+	mi := &file_assessment_proto_msgTypes[6]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use MetricData.ProtoReflect.Descriptor instead.
+func (*MetricData) Descriptor() ([]byte, []int) {
+	return file_assessment_proto_rawDescGZIP(), []int{6}
+}
+
+func (x *MetricData) GetTargetValue() *structpb.Value {
+	if x != nil {
+		return x.TargetValue
+	}
+	return nil
+}
+
+func (x *MetricData) GetOperator() string {
+	if x != nil {
+		return x.Operator
+	}
+	return ""
 }
 
 var File_assessment_proto protoreflect.FileDescriptor
@@ -347,33 +453,57 @@ var file_assessment_proto_rawDesc = []byte{
 	0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61,
 	0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f,
 	0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70,
-	0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0e, 0x65, 0x76, 0x69, 0x64, 0x65, 0x6e,
-	0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x3a, 0x0a, 0x18, 0x54, 0x72, 0x69, 0x67,
-	0x67, 0x65, 0x72, 0x41, 0x73, 0x73, 0x65, 0x73, 0x73, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71,
-	0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x6f, 0x6d, 0x65, 0x4f, 0x70, 0x74, 0x69,
-	0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6f, 0x6d, 0x65, 0x4f, 0x70,
-	0x74, 0x69, 0x6f, 0x6e, 0x22, 0x1e, 0x0a, 0x1c, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x73, 0x65,
-	0x73, 0x73, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x52, 0x65, 0x71,
-	0x75, 0x65, 0x73, 0x74, 0x22, 0x4c, 0x0a, 0x1d, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x73, 0x65,
-	0x73, 0x73, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x52, 0x65, 0x73,
-	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73,
-	0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x69, 0x74,
-	0x6f, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c,
-	0x74, 0x73, 0x22, 0x48, 0x0a, 0x15, 0x41, 0x73, 0x73, 0x65, 0x73, 0x73, 0x45, 0x76, 0x69, 0x64,
-	0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x08, 0x65,
-	0x76, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e,
-	0x63, 0x6c, 0x6f, 0x75, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x2e, 0x45, 0x76, 0x69, 0x64, 0x65, 0x6e,
-	0x63, 0x65, 0x52, 0x08, 0x65, 0x76, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x22, 0x30, 0x0a, 0x16,
-	0x41, 0x73, 0x73, 0x65, 0x73, 0x73, 0x45, 0x76, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65,
-	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73,
-	0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x64,
-	0x0a, 0x06, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f,
-	0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72,
-	0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6d,
-	0x70, 0x6c, 0x69, 0x61, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f,
-	0x6d, 0x70, 0x6c, 0x69, 0x61, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x74, 0x72, 0x69,
-	0x63, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x72,
-	0x69, 0x63, 0x49, 0x64, 0x32, 0xae, 0x03, 0x0a, 0x0a, 0x41, 0x73, 0x73, 0x65, 0x73, 0x73, 0x6d,
+	0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
+	0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74,
+	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
+	0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0e, 0x65, 0x76, 0x69, 0x64, 0x65, 0x6e, 0x63,
+	0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x3a, 0x0a, 0x18, 0x54, 0x72, 0x69, 0x67, 0x67,
+	0x65, 0x72, 0x41, 0x73, 0x73, 0x65, 0x73, 0x73, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x6f, 0x6d, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f,
+	0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6f, 0x6d, 0x65, 0x4f, 0x70, 0x74,
+	0x69, 0x6f, 0x6e, 0x22, 0x1e, 0x0a, 0x1c, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x73, 0x65, 0x73,
+	0x73, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x22, 0x4c, 0x0a, 0x1d, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x73, 0x73, 0x65, 0x73,
+	0x73, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70,
+	0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18,
+	0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x69, 0x74, 0x6f,
+	0x72, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74,
+	0x73, 0x22, 0x48, 0x0a, 0x15, 0x41, 0x73, 0x73, 0x65, 0x73, 0x73, 0x45, 0x76, 0x69, 0x64, 0x65,
+	0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x08, 0x65, 0x76,
+	0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63,
+	0x6c, 0x6f, 0x75, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x2e, 0x45, 0x76, 0x69, 0x64, 0x65, 0x6e, 0x63,
+	0x65, 0x52, 0x08, 0x65, 0x76, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x22, 0x30, 0x0a, 0x16, 0x41,
+	0x73, 0x73, 0x65, 0x73, 0x73, 0x45, 0x76, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73,
+	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xbf, 0x02,
+	0x0a, 0x06, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65,
+	0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f,
+	0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69,
+	0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
+	0x6d, 0x70, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18,
+	0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x49, 0x64, 0x12,
+	0x36, 0x0a, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x69, 0x74, 0x6f, 0x72,
+	0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0a, 0x6d, 0x65, 0x74,
+	0x72, 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6c,
+	0x69, 0x61, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x70,
+	0x6c, 0x69, 0x61, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x76, 0x69, 0x64, 0x65, 0x6e, 0x63,
+	0x65, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x76, 0x69, 0x64,
+	0x65, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72,
+	0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x73,
+	0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x36, 0x0a, 0x17, 0x6e, 0x6f, 0x6e, 0x5f, 0x63,
+	0x6f, 0x6d, 0x70, 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e,
+	0x74, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x6e, 0x6f, 0x6e, 0x43, 0x6f, 0x6d,
+	0x70, 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22,
+	0x63, 0x0a, 0x0a, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x12, 0x39, 0x0a,
+	0x0c, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0b, 0x74, 0x61, 0x72,
+	0x67, 0x65, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x70, 0x65, 0x72,
+	0x61, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x70, 0x65, 0x72,
+	0x61, 0x74, 0x6f, 0x72, 0x32, 0xae, 0x03, 0x0a, 0x0a, 0x41, 0x73, 0x73, 0x65, 0x73, 0x73, 0x6d,
 	0x65, 0x6e, 0x74, 0x12, 0x52, 0x0a, 0x11, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x41, 0x73,
 	0x73, 0x65, 0x73, 0x73, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x23, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64,
 	0x69, 0x74, 0x6f, 0x72, 0x2e, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x41, 0x73, 0x73, 0x65,
@@ -416,7 +546,7 @@ func file_assessment_proto_rawDescGZIP() []byte {
 	return file_assessment_proto_rawDescData
 }
 
-var file_assessment_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
+var file_assessment_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
 var file_assessment_proto_goTypes = []interface{}{
 	(*TriggerAssessmentRequest)(nil),      // 0: clouditor.TriggerAssessmentRequest
 	(*ListAssessmentResultsRequest)(nil),  // 1: clouditor.ListAssessmentResultsRequest
@@ -424,25 +554,31 @@ var file_assessment_proto_goTypes = []interface{}{
 	(*AssessEvidenceRequest)(nil),         // 3: clouditor.AssessEvidenceRequest
 	(*AssessEvidenceResponse)(nil),        // 4: clouditor.AssessEvidenceResponse
 	(*Result)(nil),                        // 5: clouditor.Result
-	(*evidence.Evidence)(nil),             // 6: clouditor.Evidence
-	(*emptypb.Empty)(nil),                 // 7: google.protobuf.Empty
+	(*MetricData)(nil),                    // 6: clouditor.MetricData
+	(*evidence.Evidence)(nil),             // 7: clouditor.Evidence
+	(*timestamppb.Timestamp)(nil),         // 8: google.protobuf.Timestamp
+	(*structpb.Value)(nil),                // 9: google.protobuf.Value
+	(*emptypb.Empty)(nil),                 // 10: google.protobuf.Empty
 }
 var file_assessment_proto_depIdxs = []int32{
-	5, // 0: clouditor.ListAssessmentResultsResponse.results:type_name -> clouditor.Result
-	6, // 1: clouditor.AssessEvidenceRequest.evidence:type_name -> clouditor.Evidence
-	0, // 2: clouditor.Assessment.TriggerAssessment:input_type -> clouditor.TriggerAssessmentRequest
-	1, // 3: clouditor.Assessment.ListAssessmentResults:input_type -> clouditor.ListAssessmentResultsRequest
-	3, // 4: clouditor.Assessment.AssessEvidence:input_type -> clouditor.AssessEvidenceRequest
-	6, // 5: clouditor.Assessment.AssessEvidences:input_type -> clouditor.Evidence
-	7, // 6: clouditor.Assessment.TriggerAssessment:output_type -> google.protobuf.Empty
-	2, // 7: clouditor.Assessment.ListAssessmentResults:output_type -> clouditor.ListAssessmentResultsResponse
-	4, // 8: clouditor.Assessment.AssessEvidence:output_type -> clouditor.AssessEvidenceResponse
-	7, // 9: clouditor.Assessment.AssessEvidences:output_type -> google.protobuf.Empty
-	6, // [6:10] is the sub-list for method output_type
-	2, // [2:6] is the sub-list for method input_type
-	2, // [2:2] is the sub-list for extension type_name
-	2, // [2:2] is the sub-list for extension extendee
-	0, // [0:2] is the sub-list for field type_name
+	5,  // 0: clouditor.ListAssessmentResultsResponse.results:type_name -> clouditor.Result
+	7,  // 1: clouditor.AssessEvidenceRequest.evidence:type_name -> clouditor.Evidence
+	8,  // 2: clouditor.Result.timestamp:type_name -> google.protobuf.Timestamp
+	6,  // 3: clouditor.Result.metric_data:type_name -> clouditor.MetricData
+	9,  // 4: clouditor.MetricData.target_value:type_name -> google.protobuf.Value
+	0,  // 5: clouditor.Assessment.TriggerAssessment:input_type -> clouditor.TriggerAssessmentRequest
+	1,  // 6: clouditor.Assessment.ListAssessmentResults:input_type -> clouditor.ListAssessmentResultsRequest
+	3,  // 7: clouditor.Assessment.AssessEvidence:input_type -> clouditor.AssessEvidenceRequest
+	7,  // 8: clouditor.Assessment.AssessEvidences:input_type -> clouditor.Evidence
+	10, // 9: clouditor.Assessment.TriggerAssessment:output_type -> google.protobuf.Empty
+	2,  // 10: clouditor.Assessment.ListAssessmentResults:output_type -> clouditor.ListAssessmentResultsResponse
+	4,  // 11: clouditor.Assessment.AssessEvidence:output_type -> clouditor.AssessEvidenceResponse
+	10, // 12: clouditor.Assessment.AssessEvidences:output_type -> google.protobuf.Empty
+	9,  // [9:13] is the sub-list for method output_type
+	5,  // [5:9] is the sub-list for method input_type
+	5,  // [5:5] is the sub-list for extension type_name
+	5,  // [5:5] is the sub-list for extension extendee
+	0,  // [0:5] is the sub-list for field type_name
 }
 
 func init() { file_assessment_proto_init() }
@@ -523,6 +659,18 @@ func file_assessment_proto_init() {
 				return nil
 			}
 		}
+		file_assessment_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*MetricData); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
 	}
 	type x struct{}
 	out := protoimpl.TypeBuilder{
@@ -530,7 +678,7 @@ func file_assessment_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_assessment_proto_rawDesc,
 			NumEnums:      0,
-			NumMessages:   6,
+			NumMessages:   7,
 			NumExtensions: 0,
 			NumServices:   1,
 		},
diff --git a/api/assessment/assessment_grpc.pb.go b/api/assessment/assessment_grpc.pb.go
index 371a364a..50c46b78 100644
--- a/api/assessment/assessment_grpc.pb.go
+++ b/api/assessment/assessment_grpc.pb.go
@@ -23,15 +23,15 @@ type AssessmentClient interface {
 	// Triggers the assessment. Part of the private API,
 	// not exposed as REST.
 	TriggerAssessment(ctx context.Context, in *TriggerAssessmentRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
-	// TODO(all): Part of public API because external entities (mainly discoveries) can use it?
-	// List the latest set of assessment results. Part of the public API,
-	// also exposed as REST
+	// TODO(all): Part of public API because external entities (mainly
+	// discoveries) can use it? List the latest set of assessment results. Part of
+	// the public API, also exposed as REST
 	ListAssessmentResults(ctx context.Context, in *ListAssessmentResultsRequest, opts ...grpc.CallOption) (*ListAssessmentResultsResponse, error)
 	// Assesses the evidence sent by discovery. Part of the public API,
 	// also exposed as REST
 	AssessEvidence(ctx context.Context, in *AssessEvidenceRequest, opts ...grpc.CallOption) (*AssessEvidenceResponse, error)
-	// Assesses stream of evidences coming from the discovery. Part of the public API,
-	// not exposed as REST
+	// Assesses stream of evidences coming from the discovery. Part of the public
+	// API, not exposed as REST
 	AssessEvidences(ctx context.Context, opts ...grpc.CallOption) (Assessment_AssessEvidencesClient, error)
 }
 
@@ -111,15 +111,15 @@ type AssessmentServer interface {
 	// Triggers the assessment. Part of the private API,
 	// not exposed as REST.
 	TriggerAssessment(context.Context, *TriggerAssessmentRequest) (*emptypb.Empty, error)
-	// TODO(all): Part of public API because external entities (mainly discoveries) can use it?
-	// List the latest set of assessment results. Part of the public API,
-	// also exposed as REST
+	// TODO(all): Part of public API because external entities (mainly
+	// discoveries) can use it? List the latest set of assessment results. Part of
+	// the public API, also exposed as REST
 	ListAssessmentResults(context.Context, *ListAssessmentResultsRequest) (*ListAssessmentResultsResponse, error)
 	// Assesses the evidence sent by discovery. Part of the public API,
 	// also exposed as REST
 	AssessEvidence(context.Context, *AssessEvidenceRequest) (*AssessEvidenceResponse, error)
-	// Assesses stream of evidences coming from the discovery. Part of the public API,
-	// not exposed as REST
+	// Assesses stream of evidences coming from the discovery. Part of the public
+	// API, not exposed as REST
 	AssessEvidences(Assessment_AssessEvidencesServer) error
 	mustEmbedUnimplementedAssessmentServer()
 }
diff --git a/api/assessment/metric.pb.go b/api/assessment/metric.pb.go
index e44462cb..46b665b8 100644
--- a/api/assessment/metric.pb.go
+++ b/api/assessment/metric.pb.go
@@ -107,7 +107,7 @@ type Metric struct {
 	unknownFields protoimpl.UnknownFields
 
 	// Required. The unique identifier of the metric.
-	Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
+	Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
 	// Required. The human readable name of the metric.
 	Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
 	// The description of the metric
@@ -152,11 +152,11 @@ func (*Metric) Descriptor() ([]byte, []int) {
 	return file_metric_proto_rawDescGZIP(), []int{0}
 }
 
-func (x *Metric) GetId() int32 {
+func (x *Metric) GetId() string {
 	if x != nil {
 		return x.Id
 	}
-	return 0
+	return ""
 }
 
 func (x *Metric) GetName() string {
@@ -456,7 +456,7 @@ var file_metric_proto_rawDesc = []byte{
 	0x63, 0x6c, 0x6f, 0x75, 0x64, 0x69, 0x74, 0x6f, 0x72, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
 	0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63,
 	0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf0, 0x01, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x72,
-	0x69, 0x63, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02,
+	0x69, 0x63, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02,
 	0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
 	0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69,
 	0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73,
diff --git a/api/evidence/evidence.go b/api/evidence/evidence.go
new file mode 100644
index 00000000..5091433f
--- /dev/null
+++ b/api/evidence/evidence.go
@@ -0,0 +1,50 @@
+package evidence
+
+import "errors"
+
+var (
+	ErrNotValidResource    = errors.New("resource in evidence is not a map")
+	ErrResourceNotStruct   = errors.New("resource in evidence is not struct value")
+	ErrResourceNotMap      = errors.New("resource in evidence is not a map")
+	ErrResourceIdMissing   = errors.New("resource in evidence is missing the id field")
+	ErrResourceIdNotString = errors.New("resource id in evidence is not a string")
+	ErrToolIdMissing       = errors.New("tool id in evidence is missing")
+	ErrTimestampMissing    = errors.New("timestamp in evidence is missing")
+)
+
+// Validate validates the evidence according to several required fields
+func (evidence *Evidence) Validate() (resourceId string, err error) {
+	if evidence.Resource == nil {
+		return "", ErrNotValidResource
+	}
+
+	value := evidence.Resource.GetStructValue()
+	if value == nil {
+		return "", ErrResourceNotStruct
+	}
+
+	m := evidence.Resource.GetStructValue().AsMap()
+	if m == nil {
+		return "", ErrResourceNotMap
+	}
+
+	field, ok := m["id"]
+	if !ok {
+		return "", ErrResourceIdMissing
+	}
+
+	resourceId, ok = field.(string)
+	if !ok {
+		return "", ErrResourceIdNotString
+	}
+
+	if evidence.ToolId == "" {
+		return "", ErrToolIdMissing
+	}
+
+	if evidence.Timestamp == nil {
+		return "", ErrTimestampMissing
+	}
+
+	return
+}
diff --git a/api/evidence/evidence.pb.go b/api/evidence/evidence.pb.go
index be8c4b89..9a205983 100644
--- a/api/evidence/evidence.pb.go
+++ b/api/evidence/evidence.pb.go
@@ -59,20 +59,18 @@ type Evidence struct {
 
 	// the ID in a uuid format
 	Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
-	// reference to a service this evidence was gathered from
-	ServiceId string `protobuf:"bytes,2,opt,name=service_id,json=serviceId,proto3" json:"service_id,omitempty"`
-	// reference to the resource this evidence was gathered from
-	ResourceId string `protobuf:"bytes,3,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"`
-	// TODO: replace with google/type/date.proto timestamp.proto or date.proto?
-	Timestamp         *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
-	ApplicableMetrics []int32                `protobuf:"varint,5,rep,packed,name=applicable_metrics,json=applicableMetrics,proto3" json:"applicable_metrics,omitempty"`
-	// "raw" evidence (for the auditor), for example the raw JSON response from
-	// the API. This does not follow a defined schema
-	Raw string `protobuf:"bytes,6,opt,name=raw,proto3" json:"raw,omitempty"`
-	// optional; a semantic representation of the Cloud resource according to our
-	// defined ontology. a JSON serialized node of our semantic graph. This may be
-	// Clouditor-specific.
-	Resource *structpb.Value `protobuf:"bytes,7,opt,name=resource,proto3" json:"resource,omitempty"`
+	// time of evidence creation
+	Timestamp *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+	// Reference to a service this evidence was gathered from
+	ServiceId string `protobuf:"bytes,3,opt,name=service_id,json=serviceId,proto3" json:"service_id,omitempty"`
+	// Reference to the tool which provided the evidence
+	ToolId string `protobuf:"bytes,4,opt,name=tool_id,json=toolId,proto3" json:"tool_id,omitempty"`
+	// Contains the evidence in its original form without following a defined
+	// schema, e.g. the raw JSON
+	Raw string `protobuf:"bytes,5,opt,name=raw,proto3" json:"raw,omitempty"`
+	// Semantic representation of the Cloud resource according to our defined
+	// ontology
+	Resource *structpb.Value `protobuf:"bytes,6,opt,name=resource,proto3" json:"resource,omitempty"`
 }
 
 func (x *Evidence) Reset() {
@@ -114,32 +112,25 @@ func (x *Evidence) GetId() string {
 	return ""
 }
 
-func (x *Evidence) GetServiceId() string {
+func (x *Evidence) GetTimestamp() *timestamppb.Timestamp {
 	if x != nil {
-		return x.ServiceId
+		return x.Timestamp
 	}
-	return ""
+	return nil
 }
 
-func (x *Evidence) GetResourceId() string {
+func (x *Evidence) GetServiceId() string {
 	if x != nil {
-		return x.ResourceId
+		return x.ServiceId
 	}
 	return ""
 }
 
-func (x *Evidence) GetTimestamp() *timestamppb.Timestamp {
+func (x *Evidence) GetToolId() string {
 	if x != nil {
-		return x.Timestamp
-	}
-	return nil
-}
-
-func (x *Evidence) GetApplicableMetrics() []int32 {
-	if x != nil {
-		return x.ApplicableMetrics
+		return x.ToolId
 	}
-	return nil
+	return ""
 }
 
 func (x *Evidence) GetRaw() string {
@@ -164,25 +155,22 @@ var file_evidence_proto_rawDesc = []byte{
 	0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72,
 	0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
 	0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73,
-	0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x89, 0x02, 0x0a, 0x08, 0x45,
+	0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd2, 0x01, 0x0a, 0x08, 0x45,
 	0x76, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x69,
-	0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x72,
-	0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72,
-	0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x73,
-	0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73,
-	0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f,
+	0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73,
+	0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f,
 	0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d,
 	0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
-	0x70, 0x12, 0x2d, 0x0a, 0x12, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x62, 0x6c, 0x65, 0x5f,
-	0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x05, 0x52, 0x11, 0x61,
-	0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
-	0x12, 0x10, 0x0a, 0x03, 0x72, 0x61, 0x77, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72,
-	0x61, 0x77, 0x12, 0x32, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x07,
-	0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
-	0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, 0x72, 0x65,
-	0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x0e, 0x5a, 0x0c, 0x61, 0x70, 0x69, 0x2f, 0x65, 0x76,
-	0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18,
+	0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64,
+	0x12, 0x17, 0x0a, 0x07, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x06, 0x74, 0x6f, 0x6f, 0x6c, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x61, 0x77,
+	0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x61, 0x77, 0x12, 0x32, 0x0a, 0x08, 0x72,
+	0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e,
+	0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
+	0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42,
+	0x0e, 0x5a, 0x0c, 0x61, 0x70, 0x69, 0x2f, 0x65, 0x76, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x62,
+	0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
diff --git a/api/orchestrator/orchestrator.pb.go b/api/orchestrator/orchestrator.pb.go
index 0171ff63..7da1332c 100644
--- a/api/orchestrator/orchestrator.pb.go
+++ b/api/orchestrator/orchestrator.pb.go
@@ -477,7 +477,7 @@ type GetMetricsRequest struct {
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	MetricId int32 `protobuf:"varint,1,opt,name=metric_id,json=metricId,proto3" json:"metric_id,omitempty"`
+	MetricId string `protobuf:"bytes,1,opt,name=metric_id,json=metricId,proto3" json:"metric_id,omitempty"`
 }
 
 func (x *GetMetricsRequest) Reset() {
@@ -512,11 +512,11 @@ func (*GetMetricsRequest) Descriptor() ([]byte, []int) {
 	return file_orchestrator_proto_rawDescGZIP(), []int{8}
 }
 
-func (x *GetMetricsRequest) GetMetricId() int32 {
+func (x *GetMetricsRequest) GetMetricId() string {
 	if x != nil {
 		return x.MetricId
 	}
-	return 0
+	return ""
 }
 
 type ListMetricsRequest struct {
@@ -805,7 +805,7 @@ var file_orchestrator_proto_rawDesc = []byte{
 	0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
 	0x65, 0x22, 0x30, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52,
 	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
-	0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x72, 0x69,
+	0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x72, 0x69,
 	0x63, 0x49, 0x64, 0x22, 0x14, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69,
 	0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x42, 0x0a, 0x13, 0x4c, 0x69, 0x73,
 	0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
diff --git a/api/orchestrator/orchestrator.pb.gw.go b/api/orchestrator/orchestrator.pb.gw.go
index c268ef9d..97a338be 100644
--- a/api/orchestrator/orchestrator.pb.gw.go
+++ b/api/orchestrator/orchestrator.pb.gw.go
@@ -393,7 +393,7 @@ func request_Orchestrator_GetMetric_0(ctx context.Context, marshaler runtime.Mar
 		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "metric_id")
 	}
 
-	protoReq.MetricId, err = runtime.Int32(val)
+	protoReq.MetricId, err = runtime.String(val)
 	if err != nil {
 		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "metric_id", err)
 	}
@@ -419,7 +419,7 @@ func local_request_Orchestrator_GetMetric_0(ctx context.Context, marshaler runti
 		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "metric_id")
 	}
 
-	protoReq.MetricId, err = runtime.Int32(val)
+	protoReq.MetricId, err = runtime.String(val)
 	if err != nil {
 		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "metric_id", err)
 	}
diff --git a/cli/commands/assessment/assessment.go b/cli/commands/assessment/assessment.go
index f35119b9..4ba1b205 100644
--- a/cli/commands/assessment/assessment.go
+++ b/cli/commands/assessment/assessment.go
@@ -38,7 +38,7 @@ import (
 func NewListResultsCommand() *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "list",
-		Short: "Lists all asssessment results",
+		Short: "Lists all assessment results",
 		RunE: func(cmd *cobra.Command, args []string) error {
 			var (
 				err     error
diff --git a/cli/commands/metric/metric.go b/cli/commands/metric/metric.go
index 409d5df1..4411d49f 100644
--- a/cli/commands/metric/metric.go
+++ b/cli/commands/metric/metric.go
@@ -28,7 +28,6 @@ package metric
 import (
 	"context"
 	"fmt"
-	"strconv"
 
 	"clouditor.io/clouditor/api/assessment"
 	"clouditor.io/clouditor/api/orchestrator"
@@ -89,15 +88,9 @@ func NewGetMetricCommand() *cobra.Command {
 
 			client = orchestrator.NewOrchestratorClient(session)
 
-			metricID, err := strconv.Atoi(args[0])
-			metricIDInt32 := int32(metricID)
+			metricID := args[0]
 
-			if err != nil {
-				fmt.Printf("Error while parsing string to int32")
-				return nil
-			}
-
-			res, err = client.GetMetric(context.Background(), &orchestrator.GetMetricsRequest{MetricId: metricIDInt32})
+			res, err = client.GetMetric(context.Background(), &orchestrator.GetMetricsRequest{MetricId: metricID})
 
 			return session.HandleResponse(res, err)
 		},
diff --git a/cli/commands/tool/tool.go b/cli/commands/tool/tool.go
index b337ac26..b534c938 100644
--- a/cli/commands/tool/tool.go
+++ b/cli/commands/tool/tool.go
@@ -92,7 +92,7 @@ func NewShowToolCommand() *cobra.Command {
 			}
 
 			if conn, err = grpc.Dial(session.URL, grpc.WithInsecure()); err != nil {
-				return fmt.Errorf("could not connect: %v", err)
+				return fmt.Errorf("could not connect: %w", err)
 			}
 
 			client = orchestrator.NewOrchestratorClient(conn)
diff --git a/cli/session.go b/cli/session.go
index 716e3633..139da6ff 100644
--- a/cli/session.go
+++ b/cli/session.go
@@ -82,7 +82,7 @@ func NewSession(url string, opts ...grpc.DialOption) (session *Session, err erro
 	}
 
 	if session.ClientConn, err = grpc.Dial(session.URL, opts...); err != nil {
-		return nil, fmt.Errorf("could not connect: %v", err)
+		return nil, fmt.Errorf("could not connect: %w", err)
 	}
 
 	return session, nil
@@ -111,7 +111,7 @@ func ContinueSession() (session *Session, err error) {
 	}
 
 	if session.ClientConn, err = grpc.Dial(session.URL, grpc.WithInsecure()); err != nil {
-		return nil, fmt.Errorf("could not connect: %v", err)
+		return nil, fmt.Errorf("could not connect: %w", err)
 	}
 
 	return session, nil
@@ -282,7 +282,7 @@ func getMetrics(toComplete string) []string {
 
 	var metrics []string
 	for _, v := range res.Metrics {
-		metrics = append(metrics, fmt.Sprintf("%d\t%s: %s", v.Id, v.Name, v.Description))
+		metrics = append(metrics, fmt.Sprintf("%s\t%s: %s", v.Id, v.Name, v.Description))
 	}
 
 	return metrics
diff --git a/clouditor.code-workspace b/clouditor.code-workspace
index 31dc0478..fa015314 100644
--- a/clouditor.code-workspace
+++ b/clouditor.code-workspace
@@ -6,9 +6,20 @@
 	],
 	"settings": {
 		"cSpell.words": [
-			"Kubernetes",
 			"clouditor",
-			"grpc"
+			"Debugf",
+			"emptypb",
+			"garuppel",
+			"grpc",
+			"Infof",
+			"Kubernetes",
+			"lebogg",
+			"oxisto",
+			"protobuf",
+			"Rego",
+			"structpb",
+			"timestamppb",
+			"Warnf"
 		],
 		"workbench.colorCustomizations": {
 			"debugConsole.errorForeground": "#a8a8a8"
diff --git a/cmd/engine/engine.go b/cmd/engine/engine.go
index 9baac791..93f91ba3 100644
--- a/cmd/engine/engine.go
+++ b/cmd/engine/engine.go
@@ -48,11 +48,11 @@ import (
 	"clouditor.io/clouditor/cli"
 	"clouditor.io/clouditor/persistence"
 	"clouditor.io/clouditor/rest"
+	service_assessment "clouditor.io/clouditor/service/assessment"
 	service_auth "clouditor.io/clouditor/service/auth"
 	service_discovery "clouditor.io/clouditor/service/discovery"
 	service_evidenceStore "clouditor.io/clouditor/service/evidence"
 	service_orchestrator "clouditor.io/clouditor/service/orchestrator"
-	"clouditor.io/clouditor/service/standalone"
 
 	"github.com/spf13/cobra"
 	"github.com/spf13/viper"
@@ -171,7 +171,7 @@ func doCmd(_ *cobra.Command, _ []string) (err error) {
 
 	discoveryService = service_discovery.NewService()
 	orchestratorService = &service_orchestrator.Service{}
-	assessmentService = standalone.NewAssessmentServer()
+	assessmentService = service_assessment.NewService()
 	evidenceStoreService = service_evidenceStore.NewService()
 
 	authService.CreateDefaultUser(viper.GetString(APIDefaultUserFlag), viper.GetString(APIDefaultPasswordFlag))
diff --git a/openapi/assessment/openapi.yaml b/openapi/assessment/openapi.yaml
index e4a3db16..abd990e2 100644
--- a/openapi/assessment/openapi.yaml
+++ b/openapi/assessment/openapi.yaml
@@ -46,13 +46,70 @@ components:
                     type: array
                     items:
                         $ref: '#/components/schemas/Result'
+        ListValue:
+            properties:
+                values:
+                    type: array
+                    items:
+                        $ref: '#/components/schemas/Value'
+                    description: Repeated field of dynamically typed values.
+            description: '`ListValue` is a wrapper around a repeated field of values. The JSON representation for `ListValue` is JSON array.'
+        MetricData:
+            properties:
+                target_value:
+                    $ref: '#/components/schemas/Value'
+                operator:
+                    type: string
         Result:
             properties:
-                resource_id:
+                id:
+                    type: string
+                    description: Assessment result id
+                timestamp:
                     type: string
+                    description: Time of assessment
+                    format: RFC3339
+                metric_id:
+                    type: string
+                    description: Reference to the metric the assessment was based on
+                metric_data:
+                    $ref: '#/components/schemas/MetricData'
                 compliant:
                     type: boolean
-                metric_id:
-                    type: integer
-                    format: int32
+                    description: 'Compliant case: true or false'
+                evidence_id:
+                    type: string
+                    description: Reference to the assessed evidence
+                resource_id:
+                    type: string
+                    description: Reference to the resource of the assessed evidence
+                non_compliance_comments:
+                    type: string
+                    description: Some comments on the reason for non-compliance
             description: A result resource, representing the result after assessing the cloud resource with id resource_id.
+        Struct:
+            properties:
+                fields:
+                    $ref: '#/components/schemas/FieldsEntry'
+            description: '`Struct` represents a structured data value, consisting of fields which map to dynamically typed values. In some languages, `Struct` might be supported by a native representation. For example, in scripting languages like JS a struct is represented as an object. The details of that representation are described together with the proto support for the language. The JSON representation for `Struct` is JSON object.'
+        Value:
+            properties:
+                null_value:
+                    type: integer
+                    description: Represents a null value.
+                    format: enum
+                number_value:
+                    type: number
+                    description: Represents a double value.
+                    format: double
+                string_value:
+                    type: string
+                    description: Represents a string value.
+                bool_value:
+                    type: boolean
+                    description: Represents a boolean value.
+                struct_value:
+                    $ref: '#/components/schemas/Struct'
+                list_value:
+                    $ref: '#/components/schemas/ListValue'
+            description: '`Value` represents a dynamically typed value which can be either null, a number, a string, a boolean, a recursive struct value, or a list of values. A producer of value is expected to set one of that variants, absence of any variant indicates an error. The JSON representation for `Value` is JSON value.'
diff --git a/openapi/orchestrator/openapi.yaml b/openapi/orchestrator/openapi.yaml
index ea5305e7..b765255c 100644
--- a/openapi/orchestrator/openapi.yaml
+++ b/openapi/orchestrator/openapi.yaml
@@ -194,9 +194,8 @@ components:
         Metric:
             properties:
                 id:
-                    type: integer
+                    type: string
                     description: Required. The unique identifier of the metric.
-                    format: int32
                 name:
                     type: string
                     description: Required. The human readable name of the metric.
diff --git a/policies/bundles/AnomalyDetectionEnabled/data.json b/policies/bundles/AnomalyDetectionEnabled/data.json
new file mode 100644
index 00000000..05a7f81d
--- /dev/null
+++ b/policies/bundles/AnomalyDetectionEnabled/data.json
@@ -0,0 +1,4 @@
+{
+  "operator" : "==",
+  "target_value" : true
+}
\ No newline at end of file
diff --git a/policies/bundles/AnomalyDetectionEnabled/metric.rego b/policies/bundles/AnomalyDetectionEnabled/metric.rego
new file mode 100644
index 00000000..524d1636
--- /dev/null
+++ b/policies/bundles/AnomalyDetectionEnabled/metric.rego
@@ -0,0 +1,15 @@
+package clouditor
+
+default applicable = false
+
+default compliant = false
+
+ad := input.anomalyDetection
+
+applicable {
+	ad
+}
+
+compliant {
+	compare(data.operator, data.target_value, ad.enabled)
+}
diff --git a/policies/bundles/AnomalyDetectionOutput/data.json b/policies/bundles/AnomalyDetectionOutput/data.json
new file mode 100644
index 00000000..03502549
--- /dev/null
+++ b/policies/bundles/AnomalyDetectionOutput/data.json
@@ -0,0 +1,6 @@
+{
+  "operator": "==",
+  "target_value": [
+    "?"
+  ]
+}
\ No newline at end of file
diff --git a/policies/bundles/AnomalyDetectionOutput/metric.rego b/policies/bundles/AnomalyDetectionOutput/metric.rego
new file mode 100644
index 00000000..0db53f01
--- /dev/null
+++ b/policies/bundles/AnomalyDetectionOutput/metric.rego
@@ -0,0 +1,15 @@
+package clouditor
+
+default applicable = false
+
+default compliant = false
+
+ad := input.anomalyDetection
+
+applicable {
+	ad
+}
+
+compliant {
+	isIn(data.target_value, ad.output)
+}
diff --git a/policies/bundles/AnonymousAuthentication/data.json b/policies/bundles/AnonymousAuthentication/data.json
new file mode 100644
index 00000000..75d05811
--- /dev/null
+++ b/policies/bundles/AnonymousAuthentication/data.json
@@ -0,0 +1,4 @@
+{
+  "operator" : "==",
+  "target_value" : false
+}
\ No newline at end of file
diff --git a/policies/bundles/AnonymousAuthentication/metric.rego b/policies/bundles/AnonymousAuthentication/metric.rego
new file mode 100644
index 00000000..0e4e79bf
--- /dev/null
+++ b/policies/bundles/AnonymousAuthentication/metric.rego
@@ -0,0 +1,15 @@
+package clouditor
+
+default applicable = false
+
+default compliant = false
+
+cba := input.certificateBasedAuthentication
+
+applicable {
+	cba
+}
+
+compliant {
+	compare(data.operator, data.target_value, cba.enabled)
+}
diff --git a/policies/bundles/AtRestEncryptionAlgorithm/data.json b/policies/bundles/AtRestEncryptionAlgorithm/data.json
new file mode 100644
index 00000000..db85e7ec
--- /dev/null
+++ b/policies/bundles/AtRestEncryptionAlgorithm/data.json
@@ -0,0 +1,4 @@
+{
+  "operator" : "==",
+  "target_value" : "AES256"
+}
\ No newline at end of file
diff --git a/policies/bundles/AtRestEncryptionAlgorithm/metric.rego b/policies/bundles/AtRestEncryptionAlgorithm/metric.rego
new file mode 100644
index 00000000..fb350ea2
--- /dev/null
+++ b/policies/bundles/AtRestEncryptionAlgorithm/metric.rego
@@ -0,0 +1,15 @@
+package clouditor
+
+default applicable = false
+
+default compliant = false
+
+enc := input.atRestEncryption
+
+applicable {
+	enc
+}
+
+compliant {
+	compare(data.operator, data.target_value, enc.algorithm)
+}
diff --git a/policies/bundles/AtRestEncryptionEnabled/data.json b/policies/bundles/AtRestEncryptionEnabled/data.json
new file mode 100644
index 00000000..05a7f81d
--- /dev/null
+++ b/policies/bundles/AtRestEncryptionEnabled/data.json
@@ -0,0 +1,4 @@
+{
+  "operator" : "==",
+  "target_value" : true
+}
\ No newline at end of file
diff --git a/policies/bundles/AtRestEncryptionEnabled/metric.rego b/policies/bundles/AtRestEncryptionEnabled/metric.rego
new file mode 100644
index 00000000..c2350963
--- /dev/null
+++ b/policies/bundles/AtRestEncryptionEnabled/metric.rego
@@ -0,0 +1,15 @@
+package clouditor
+
+default applicable = false
+
+default compliant = false
+
+enc := input.atRestEncryption
+
+applicable {
+	enc
+}
+
+compliant {
+	compare(data.operator, data.target_value, enc.enabled)
+}
diff --git a/policies/bundles/AutomaticUpdatesEnabled/data.json b/policies/bundles/AutomaticUpdatesEnabled/data.json
new file mode 100644
index 00000000..05a7f81d
--- /dev/null
+++ b/policies/bundles/AutomaticUpdatesEnabled/data.json
@@ -0,0 +1,4 @@
+{
+  "operator" : "==",
+  "target_value" : true
+}
\ No newline at end of file
diff --git a/policies/bundles/AutomaticUpdatesEnabled/metric.rego b/policies/bundles/AutomaticUpdatesEnabled/metric.rego
new file mode 100644
index 00000000..ad202323
--- /dev/null
+++ b/policies/bundles/AutomaticUpdatesEnabled/metric.rego
@@ -0,0 +1,15 @@
+package clouditor
+
+default applicable = false
+
+default compliant = false
+
+autoUpdates := input.automaticUpdates
+
+applicable {
+	autoUpdates
+}
+
+compliant {
+	compare(data.operator, data.target_value, autoUpdates.enabled)
+}
diff --git a/policies/bundles/AutomaticUpdatesInterval/data.json b/policies/bundles/AutomaticUpdatesInterval/data.json
new file mode 100644
index 00000000..6317394b
--- /dev/null
+++ b/policies/bundles/AutomaticUpdatesInterval/data.json
@@ -0,0 +1,4 @@
+{
+  "operator" : "<=",
+  "target_value" : 168
+}
\ No newline at end of file
diff --git a/policies/bundles/AutomaticUpdatesInterval/metric.rego b/policies/bundles/AutomaticUpdatesInterval/metric.rego
new file mode 100644
index 00000000..b6383738
--- /dev/null
+++ b/policies/bundles/AutomaticUpdatesInterval/metric.rego
@@ -0,0 +1,15 @@
+package clouditor
+
+default applicable = false
+
+default compliant = false
+
+autoUpdates := input.automaticUpdates
+
+applicable {
+	autoUpdates
+}
+
+compliant {
+	compare(data.operator, data.target_value, autoUpdates.interval)
+}
diff --git a/policies/bundles/AutomaticUpdatesSecurityOnly/data.json b/policies/bundles/AutomaticUpdatesSecurityOnly/data.json
new file mode 100644
index 00000000..05a7f81d
--- /dev/null
+++ b/policies/bundles/AutomaticUpdatesSecurityOnly/data.json
@@ -0,0 +1,4 @@
+{
+  "operator" : "==",
+  "target_value" : true
+}
\ No newline at end of file
diff --git a/policies/bundles/AutomaticUpdatesSecurityOnly/metric.rego b/policies/bundles/AutomaticUpdatesSecurityOnly/metric.rego
new file mode 100644
index 00000000..9543b43e
--- /dev/null
+++ b/policies/bundles/AutomaticUpdatesSecurityOnly/metric.rego
@@ -0,0 +1,15 @@
+package clouditor
+
+default applicable = false
+
+default compliant = false
+
+autoUpdates := input.automaticUpdates
+
+applicable {
+	autoUpdates
+}
+
+compliant {
+	compare(data.operator, data.target_value, autoUpdates.securityOnly)
+}
diff --git a/policies/bundles/BootLoggingEnabled/data.json b/policies/bundles/BootLoggingEnabled/data.json
new file mode 100644
index 00000000..05a7f81d
--- /dev/null
+++ b/policies/bundles/BootLoggingEnabled/data.json
@@ -0,0 +1,4 @@
+{
+  "operator" : "==",
+  "target_value" : true
+}
\ No newline at end of file
diff --git a/policies/bundles/BootLoggingEnabled/metric.rego b/policies/bundles/BootLoggingEnabled/metric.rego
new file mode 100644
index 00000000..ad4809e1
--- /dev/null
+++ b/policies/bundles/BootLoggingEnabled/metric.rego
@@ -0,0 +1,15 @@
+package clouditor
+
+default applicable = false
+
+default compliant = false
+
+bootLog := input.bootLog
+
+applicable {
+	bootLog
+}
+
+compliant {
+	compare(data.operator, data.target_value, bootLog.enabled)
+}
diff --git a/policies/bundles/BootLoggingOutput/data.json b/policies/bundles/BootLoggingOutput/data.json
new file mode 100644
index 00000000..cba67784
--- /dev/null
+++ b/policies/bundles/BootLoggingOutput/data.json
@@ -0,0 +1,4 @@
+{
+  "operator" : "==",
+  "target_value" : ["SomeResourceId1","SomeResourceId2"]
+}
\ No newline at end of file
diff --git a/policies/bundles/BootLoggingOutput/metric.rego b/policies/bundles/BootLoggingOutput/metric.rego
new file mode 100644
index 00000000..20c2ac6d
--- /dev/null
+++ b/policies/bundles/BootLoggingOutput/metric.rego
@@ -0,0 +1,17 @@
+package clouditor
+
+default applicable = false
+
+default compliant = false
+
+metricData := data.target_value
+
+bootLog := input.bootLog
+
+applicable {
+	bootLog
+}
+
+compliant {
+	isIn(data.target_value, bootLog.output)
+}
diff --git a/policies/bundles/BootLoggingRetention/data.json b/policies/bundles/BootLoggingRetention/data.json
new file mode 100644
index 00000000..019dfa19
--- /dev/null
+++ b/policies/bundles/BootLoggingRetention/data.json
@@ -0,0 +1,4 @@
+{
+  "operator" : ">=",
+  "target_value" : 35
+}
\ No newline at end of file
diff --git a/policies/bundles/BootLoggingRetention/metric.rego b/policies/bundles/BootLoggingRetention/metric.rego
new file mode 100644
index 00000000..50c89f1c
--- /dev/null
+++ b/policies/bundles/BootLoggingRetention/metric.rego
@@ -0,0 +1,15 @@
+package clouditor
+
+default applicable = false
+
+default compliant = false
+
+bootLog := input.bootLog
+
+applicable {
+	bootLog
+}
+
+compliant {
+	compare(data.operator, data.target_value, bootLog.retentionPeriod)
+}
diff --git a/policies/bundles/BroadAssignments/data.json b/policies/bundles/BroadAssignments/data.json
new file mode 100644
index 00000000..671f81b6
--- /dev/null
+++ b/policies/bundles/BroadAssignments/data.json
@@ -0,0 +1,4 @@
+{
+  "operator" : "<=",
+  "target_value" : 0.2
+}
\ No newline at end of file
diff --git a/policies/bundles/BroadAssignments/metric.rego b/policies/bundles/BroadAssignments/metric.rego
new file mode 100644
index 00000000..9c3a1ff7
--- /dev/null
+++ b/policies/bundles/BroadAssignments/metric.rego
@@ -0,0 +1,17 @@
+package clouditor
+
+default applicable = false
+
+default compliant = false
+
+# TODO(lebogg): Not yet in VOC. Check if `rBAC` is correct representation in JSON
+rbac := input.rBAC
+
+applicable {
+	rbac
+}
+
+compliant {
+	# TODO(all): Target value ?
+	compare(data.operator, data.target_value, rbac.broadAssignments)
+}
diff --git a/policies/bundles/CustomerKeyEncryption/data.json b/policies/bundles/CustomerKeyEncryption/data.json
new file mode 100644
index 00000000..05a7f81d
--- /dev/null
+++ b/policies/bundles/CustomerKeyEncryption/data.json
@@ -0,0 +1,4 @@
+{
+  "operator" : "==",
+  "target_value" : true
+}
\ No newline at end of file
diff --git a/policies/bundles/CustomerKeyEncryption/metric.rego b/policies/bundles/CustomerKeyEncryption/metric.rego
new file mode 100644
index 00000000..195b97b2
--- /dev/null
+++ b/policies/bundles/CustomerKeyEncryption/metric.rego
@@ -0,0 +1,16 @@
+package clouditor
+
+default applicable = false
+
+default compliant = false
+
+enc := input.atRestEncryption
+
+applicable {
+	enc
+}
+
+compliant {
+	# Check if keyUrl is set. It is only set in the customer key encryption case.
+	enc.keyUrl
+}
diff --git a/policies/bundles/L3FirewallEnabled/data.json b/policies/bundles/L3FirewallEnabled/data.json
new file mode 100644
index 00000000..05a7f81d
--- /dev/null
+++ b/policies/bundles/L3FirewallEnabled/data.json
@@ -0,0 +1,4 @@
+{
+  "operator" : "==",
+  "target_value" : true
+}
\ No newline at end of file
diff --git a/policies/bundles/L3FirewallEnabled/metric.rego b/policies/bundles/L3FirewallEnabled/metric.rego
new file mode 100644
index 00000000..df826e56
--- /dev/null
+++ b/policies/bundles/L3FirewallEnabled/metric.rego
@@ -0,0 +1,15 @@
+package clouditor
+
+default applicable = false
+
+default compliant = false
+
+l3f := input.l3Firewall
+
+applicable {
+	l3f
+}
+
+compliant {
+	compare(data.operator, data.target_value, l3f.enabled)
+}
diff --git a/policies/bundles/L3FirewallRestrictedPorts/data.json b/policies/bundles/L3FirewallRestrictedPorts/data.json
new file mode 100644
index 00000000..73e32a3a
--- /dev/null
+++ b/policies/bundles/L3FirewallRestrictedPorts/data.json
@@ -0,0 +1,4 @@
+{
+  "operator" : "==",
+  "target_value" : "22"
+}
\ No newline at end of file
diff --git a/policies/bundles/L3FirewallRestrictedPorts/metric.rego b/policies/bundles/L3FirewallRestrictedPorts/metric.rego
new file mode 100644
index 00000000..7f477f1e
--- /dev/null
+++ b/policies/bundles/L3FirewallRestrictedPorts/metric.rego
@@ -0,0 +1,16 @@
+package clouditor
+
+default applicable = false
+
+default compliant = false
+
+l3f := input.l3Firewall
+
+applicable {
+	l3f
+}
+
+# TODO(all): Maybe change restrictet ports to array of strings. See comment in Ontology.
+compliant {
+	compare(data.operator, data.target_value, l3f.restrictedPorts)
+}
diff --git a/policies/bundles/MalwareProtectionEnabeld/data.json b/policies/bundles/MalwareProtectionEnabeld/data.json
new file mode 100644
index 00000000..05a7f81d
--- /dev/null
+++ b/policies/bundles/MalwareProtectionEnabeld/data.json
@@ -0,0 +1,4 @@
+{
+  "operator" : "==",
+  "target_value" : true
+}
\ No newline at end of file
diff --git a/policies/bundles/MalwareProtectionEnabeld/metric.rego b/policies/bundles/MalwareProtectionEnabeld/metric.rego
new file mode 100644
index 00000000..a9c623c7
--- /dev/null
+++ b/policies/bundles/MalwareProtectionEnabeld/metric.rego
@@ -0,0 +1,15 @@
+package clouditor
+
+default applicable = false
+
+default compliant = false
+
+mp := input.malwareProtection
+
+applicable {
+	mp
+}
+
+compliant {
+	compare(data.operator, data.target_value, mp.enabled)
+}
diff --git a/policies/bundles/MalwareProtectionOutput/data.json b/policies/bundles/MalwareProtectionOutput/data.json
new file mode 100644
index 00000000..3b8959ff
--- /dev/null
+++ b/policies/bundles/MalwareProtectionOutput/data.json
@@ -0,0 +1,4 @@
+{
+  "operator" : "==",
+  "target_value" : ["SomeAnalyticsService?", "?"]
+}
\ No newline at end of file
diff --git a/policies/bundles/MalwareProtectionOutput/metric.rego b/policies/bundles/MalwareProtectionOutput/metric.rego
new file mode 100644
index 00000000..fe6b5b5b
--- /dev/null
+++ b/policies/bundles/MalwareProtectionOutput/metric.rego
@@ -0,0 +1,15 @@
+package clouditor
+
+default applicable = false
+
+default compliant = false
+
+mp := input.malwareProtection
+
+applicable {
+	mp
+}
+
+compliant {
+	isIn(data.target_value, mp.output)
+}
diff --git a/policies/bundles/MixedDuties/data.json b/policies/bundles/MixedDuties/data.json
new file mode 100644
index 00000000..671f81b6
--- /dev/null
+++ b/policies/bundles/MixedDuties/data.json
@@ -0,0 +1,4 @@
+{
+  "operator" : "<=",
+  "target_value" : 0.2
+}
\ No newline at end of file
diff --git a/policies/bundles/MixedDuties/metric.rego b/policies/bundles/MixedDuties/metric.rego
new file mode 100644
index 00000000..154a6944
--- /dev/null
+++ b/policies/bundles/MixedDuties/metric.rego
@@ -0,0 +1,17 @@
+package clouditor
+
+default applicable = false
+
+default compliant = false
+
+# TODO(lebogg): Check if `rBAC` is correct representation in JSON
+rbac := input.rBAC
+
+applicable {
+	rbac
+}
+
+compliant {
+	# TODO(all): Target value ?
+	compare(data.operator, data.target_value, rbac.mixedDuties)
+}
diff --git a/policies/bundles/MutualAuthentication/data.json b/policies/bundles/MutualAuthentication/data.json
new file mode 100644
index 00000000..05a7f81d
--- /dev/null
+++ b/policies/bundles/MutualAuthentication/data.json
@@ -0,0 +1,4 @@
+{
+  "operator" : "==",
+  "target_value" : true
+}
\ No newline at end of file
diff --git a/policies/bundles/MutualAuthentication/metric.rego b/policies/bundles/MutualAuthentication/metric.rego
new file mode 100644
index 00000000..266b5deb
--- /dev/null
+++ b/policies/bundles/MutualAuthentication/metric.rego
@@ -0,0 +1,22 @@
+package clouditor
+
+default applicable = false
+
+default compliant = false
+
+cba := input.certificateBasedAuthentication
+
+enc := input.httpEndpoint.transportEncryption
+
+applicable {
+	cba
+	enc
+}
+
+# TODO(all): Actually, in this case, data.operator and data.target_value are for the overall metric. Not single checks.
+# TODO(cont.): That would mean it is reather compliant = data.targetValue
+# TODO(lebogg): Look if we can access other evaluated metrics within this policy, e.g. TransportEncryptionEnabled
+compliant {
+	compare(data.operator, data.target_value, cba.enabled)
+	compare(data.operator, data.target_value, enc.enabled)
+}
diff --git a/policies/bundles/OSLoggingEnabled/data.json b/policies/bundles/OSLoggingEnabled/data.json
new file mode 100644
index 00000000..05a7f81d
--- /dev/null
+++ b/policies/bundles/OSLoggingEnabled/data.json
@@ -0,0 +1,4 @@
+{
+  "operator" : "==",
+  "target_value" : true
+}
\ No newline at end of file
diff --git a/policies/bundles/OSLoggingEnabled/metric.rego b/policies/bundles/OSLoggingEnabled/metric.rego
new file mode 100644
index 00000000..711ffb35
--- /dev/null
+++ b/policies/bundles/OSLoggingEnabled/metric.rego
@@ -0,0 +1,15 @@
+package clouditor
+
+default applicable = false
+
+default compliant = false
+
+OSLog := input.oSLog
+
+applicable {
+	OSLog
+}
+
+compliant {
+	compare(data.operator, data.target_value, OSLog.enabled)
+}
diff --git a/policies/bundles/OSLoggingOutput/data.json b/policies/bundles/OSLoggingOutput/data.json
new file mode 100644
index 00000000..cba67784
--- /dev/null
+++ b/policies/bundles/OSLoggingOutput/data.json
@@ -0,0 +1,4 @@
+{
+  "operator" : "==",
+  "target_value" : ["SomeResourceId1","SomeResourceId2"]
+}
\ No newline at end of file
diff --git a/policies/bundles/OSLoggingOutput/metric.rego b/policies/bundles/OSLoggingOutput/metric.rego
new file mode 100644
index 00000000..af1ad576
--- /dev/null
+++ b/policies/bundles/OSLoggingOutput/metric.rego
@@ -0,0 +1,17 @@
+package clouditor
+
+default applicable = false
+
+default compliant = false
+
+metricData := data.target_value
+
+OSLog := input.oSLog
+
+applicable {
+	OSLog
+}
+
+compliant {
+	isIn(data.target_value, OSLog.output)
+}
diff --git a/policies/bundles/OSLoggingRetention/data.json b/policies/bundles/OSLoggingRetention/data.json
new file mode 100644
index 00000000..019dfa19
--- /dev/null
+++ b/policies/bundles/OSLoggingRetention/data.json
@@ -0,0 +1,4 @@
+{
+  "operator" : ">=",
+  "target_value" : 35
+}
\ No newline at end of file
diff --git a/policies/bundles/OSLoggingRetention/metric.rego b/policies/bundles/OSLoggingRetention/metric.rego
new file mode 100644
index 00000000..606a1e0f
--- /dev/null
+++ b/policies/bundles/OSLoggingRetention/metric.rego
@@ -0,0 +1,15 @@
+package clouditor
+
+default applicable = false
+
+default compliant = false
+
+OSLog := input.oSLog
+
+applicable {
+	OSLog
+}
+
+compliant {
+	compare(data.operator, data.target_value, OSLog.retentionPeriod)
+}
diff --git a/policies/bundles/RuntimeVersion/data.json b/policies/bundles/RuntimeVersion/data.json
new file mode 100644
index 00000000..0e2a3441
--- /dev/null
+++ b/policies/bundles/RuntimeVersion/data.json
@@ -0,0 +1,17 @@
+{
+  "operator" : ">=",
+  "target_value" : [
+    {
+      "runtimeLanguage": "Java",
+      "runtimeVersion": 11
+    },
+    {
+      "runtimeLanguage": "Python",
+      "runtimeVersion": 3.8
+    },
+    {
+      "runtimeLanguage": "PHP",
+      "runtimeVersion": 7.4
+    }
+  ]
+}
\ No newline at end of file
diff --git a/policies/bundles/RuntimeVersion/metric.rego b/policies/bundles/RuntimeVersion/metric.rego
new file mode 100644
index 00000000..ad32845c
--- /dev/null
+++ b/policies/bundles/RuntimeVersion/metric.rego
@@ -0,0 +1,21 @@
+package clouditor
+
+default applicable = false
+
+default compliant = false
+
+runtimeLanguage := input.runtimeLanguage
+
+runtimeVersion := input.runtimeVersion
+
+applicable {
+	runtimeLanguage
+	runtimeVersion
+}
+
+# TODO(all): Consider to put `operator` into list of target_values for more granularity
+compliant {
+	some i
+	compare("==", data.target_value[i].runtimeLanguage, runtimeLanguage)
+	compare(data.operator, data.target_value[i].runtimeVersion, runtimeVersion)
+}
diff --git a/policies/bundles/SingleSignOnEnabled/data.json b/policies/bundles/SingleSignOnEnabled/data.json
new file mode 100644
index 00000000..05a7f81d
--- /dev/null
+++ b/policies/bundles/SingleSignOnEnabled/data.json
@@ -0,0 +1,4 @@
+{
+  "operator" : "==",
+  "target_value" : true
+}
\ No newline at end of file
diff --git a/policies/bundles/SingleSignOnEnabled/metric.rego b/policies/bundles/SingleSignOnEnabled/metric.rego
new file mode 100644
index 00000000..1189af03
--- /dev/null
+++ b/policies/bundles/SingleSignOnEnabled/metric.rego
@@ -0,0 +1,15 @@
+package clouditor
+
+default applicable = false
+
+default compliant = false
+
+sso := input.singleSignOn
+
+applicable {
+	sso
+}
+
+compliant {
+	compare(data.operator, data.target_value, sso.enabled)
+}
diff --git a/policies/bundles/TLSVersion/data.json b/policies/bundles/TLSVersion/data.json
new file mode 100644
index 00000000..cd0e7d3e
--- /dev/null
+++ b/policies/bundles/TLSVersion/data.json
@@ -0,0 +1,4 @@
+{
+  "operator" : "==",
+  "target_value" : ["TLS1.2","TLS1.3"]
+}
\ No newline at end of file
diff --git a/policies/bundles/TLSVersion/metric.rego b/policies/bundles/TLSVersion/metric.rego
new file mode 100644
index 00000000..6c02699d
--- /dev/null
+++ b/policies/bundles/TLSVersion/metric.rego
@@ -0,0 +1,21 @@
+package clouditor
+
+default compliant = false
+
+default applicable = false
+
+endpoint := input.httpEndpoint
+
+applicable {
+	endpoint
+}
+
+compliant {
+	# If target_value is a list of strings/numbers
+	isIn(data.target_value, endpoint.transportEncryption.tlsVersion)
+}
+
+compliant {
+	# If target_value is the version number represented as int/float
+	compare(data.operator, data.target_value, endpoint.transportEncryption.tlsVersion)
+}
diff --git a/policies/bundles/TransportEncryptionAlgorithm/data.json b/policies/bundles/TransportEncryptionAlgorithm/data.json
new file mode 100644
index 00000000..5b79f20d
--- /dev/null
+++ b/policies/bundles/TransportEncryptionAlgorithm/data.json
@@ -0,0 +1,4 @@
+{
+  "operator" : "==",
+  "target_value" : "TLS"
+}
\ No newline at end of file
diff --git a/policies/bundles/TransportEncryptionAlgorithm/metric.rego b/policies/bundles/TransportEncryptionAlgorithm/metric.rego
new file mode 100644
index 00000000..a4c5e3be
--- /dev/null
+++ b/policies/bundles/TransportEncryptionAlgorithm/metric.rego
@@ -0,0 +1,15 @@
+package clouditor
+
+default compliant = false
+
+default applicable = false
+
+endpoint := input.httpEndpoint
+
+applicable {
+	endpoint
+}
+
+compliant {
+	compare(data.operator, data.target_value, endpoint.transportEncryption.algorithm)
+}
diff --git a/policies/bundles/TransportEncryptionEnabled/data.json b/policies/bundles/TransportEncryptionEnabled/data.json
new file mode 100644
index 00000000..05a7f81d
--- /dev/null
+++ b/policies/bundles/TransportEncryptionEnabled/data.json
@@ -0,0 +1,4 @@
+{
+  "operator" : "==",
+  "target_value" : true
+}
\ No newline at end of file
diff --git a/policies/bundles/TransportEncryptionEnabled/metric.rego b/policies/bundles/TransportEncryptionEnabled/metric.rego
new file mode 100644
index 00000000..1b26633f
--- /dev/null
+++ b/policies/bundles/TransportEncryptionEnabled/metric.rego
@@ -0,0 +1,16 @@
+package clouditor
+
+default compliant = false
+
+default applicable = false
+
+endpoint := input.httpEndpoint
+
+applicable {
+	endpoint
+}
+
+# TODO(all): Alternatively, curly braces can be removed and a single assignment used. But for readability and consistency (having multiple compares, see mutual auth) I let it this way?
+compliant {
+	compare(data.operator, data.target_value, endpoint.transportEncryption.enabled)
+}
diff --git a/policies/bundles/TransportEncryptionEnforced/data.json b/policies/bundles/TransportEncryptionEnforced/data.json
new file mode 100644
index 00000000..05a7f81d
--- /dev/null
+++ b/policies/bundles/TransportEncryptionEnforced/data.json
@@ -0,0 +1,4 @@
+{
+  "operator" : "==",
+  "target_value" : true
+}
\ No newline at end of file
diff --git a/policies/bundles/TransportEncryptionEnforced/metric.rego b/policies/bundles/TransportEncryptionEnforced/metric.rego
new file mode 100644
index 00000000..e63f485b
--- /dev/null
+++ b/policies/bundles/TransportEncryptionEnforced/metric.rego
@@ -0,0 +1,15 @@
+package clouditor
+
+default compliant = false
+
+default applicable = false
+
+endpoint := input.httpEndpoint
+
+applicable {
+	endpoint
+}
+
+compliant {
+	compare(data.operator, data.target_value, endpoint.transportEncryption.enforced)
+}
diff --git a/policies/bundles/WebApplicationFirewallEnabled/data.json b/policies/bundles/WebApplicationFirewallEnabled/data.json
new file mode 100644
index 00000000..05a7f81d
--- /dev/null
+++ b/policies/bundles/WebApplicationFirewallEnabled/data.json
@@ -0,0 +1,4 @@
+{
+  "operator" : "==",
+  "target_value" : true
+}
\ No newline at end of file
diff --git a/policies/bundles/WebApplicationFirewallEnabled/metric.rego b/policies/bundles/WebApplicationFirewallEnabled/metric.rego
new file mode 100644
index 00000000..bd5cf62c
--- /dev/null
+++ b/policies/bundles/WebApplicationFirewallEnabled/metric.rego
@@ -0,0 +1,15 @@
+package clouditor
+
+default applicable = false
+
+default compliant = false
+
+waf := input.webApplicationFirewall
+
+applicable {
+	waf
+}
+
+compliant {
+	compare(data.operator, data.target_value, waf.enabled)
+}
diff --git a/policies/metric1.rego b/policies/metric1.rego
deleted file mode 100644
index 9755e699..00000000
--- a/policies/metric1.rego
+++ /dev/null
@@ -1,10 +0,0 @@
-package clouditor
-
-default compliant = false
-
-# this is an implementation of metric 1 (Transport Encryption)
-
-compliant {
-	tls := input.httpEndpoint.transportEncryption
-	tls.enabled == true
-}
diff --git a/policies/metric2.rego b/policies/metric2.rego
deleted file mode 100644
index 6dc6a2a2..00000000
--- a/policies/metric2.rego
+++ /dev/null
@@ -1,16 +0,0 @@
-package clouditor
-
-default compliant = false
-
-compliant {
-	tls := input.httpEndpoint.transportEncryption
-	goodVersion(tls)
-}
-
-goodVersion(tls) {
-	tls.version == "TLS 1.2"
-}
-
-goodVersion(tls) {
-	tls.version == "TLS 1.3"
-}
diff --git a/policies/operators.rego b/policies/operators.rego
new file mode 100644
index 00000000..896af9a9
--- /dev/null
+++ b/policies/operators.rego
@@ -0,0 +1,31 @@
+package clouditor
+
+compare(operator, target_value, actual_value) {
+    operator == "=="
+    target_value == actual_value
+}{
+    operator == ">="
+    actual_value >= target_value
+}{
+     operator == "<="
+     actual_value <= target_value
+}{
+     operator == "<"
+     actual_value < target_value
+}{
+     operator == ">"
+     actual_value > target_value
+}
+
+
+# Params: target_values (multiple target values), actual_value (single actual value)
+isIn(target_values, actual_value) {
+    # Assess actual value with each compliant value in target values
+	actual_value == target_values[_]
+}
+
+# Params: target_values (multiple target values), actual_values (multiple actual values)
+isIn(target_values, actual_values) {
+    # Current implementation: It is enough that one output is one of target_values
+    actual_values[_] == target_values[_]
+}
\ No newline at end of file
diff --git a/policies/policies.go b/policies/policies.go
index ed71bd4c..8cbe0dc3 100644
--- a/policies/policies.go
+++ b/policies/policies.go
@@ -29,26 +29,92 @@ import (
 	"context"
 	"errors"
 	"fmt"
+	"os"
+	"strings"
 
 	"clouditor.io/clouditor/api/evidence"
 	"github.com/open-policy-agent/opa/rego"
 )
 
-func RunEvidence(file string, evidence *evidence.Evidence) (data map[string]interface{}, err error) {
+// applicableMetrics stores a list of applicable metrics per resourceType
+var applicableMetrics = make(map[string][]string)
+
+func RunEvidence(evidence *evidence.Evidence) ([]map[string]interface{}, error) {
+	data := make([]map[string]interface{}, 0)
+	var baseDir string = "."
+
 	var m = evidence.Resource.GetStructValue().AsMap()
 
-	return RunMap(file, m)
+	var types []string
+
+	if rawTypes, ok := m["type"].([]interface{}); ok {
+		types = make([]string, len(rawTypes))
+	} else {
+		return nil, fmt.Errorf("got type '%T' but wanted '[]interface {}'. Check if resource types are specified ", rawTypes)
+	}
+	for i, v := range m["type"].([]interface{}) {
+		if t, ok := v.(string); !ok {
+			return nil, fmt.Errorf("got type '%T' but wanted 'string'", t)
+		} else {
+			types[i] = t
+		}
+	}
+
+	// TODO(lebogg): Try to optimize duplicated code
+	if key := createKey(types); applicableMetrics[key] == nil {
+		files, err := scanBundleDir(baseDir)
+		if err != nil {
+			return nil, fmt.Errorf("could not load metric bundles: %w", err)
+		}
+
+		for _, fileInfo := range files {
+			runMap, err := RunMap(baseDir, fileInfo.Name(), m)
+			if err != nil {
+				return nil, err
+			}
+
+			if runMap != nil {
+				metricId := fileInfo.Name()
+				applicableMetrics[key] = append(applicableMetrics[key], metricId)
+				runMap["metricId"] = metricId
+
+				data = append(data, runMap)
+			}
+		}
+	} else {
+		for _, metric := range applicableMetrics[key] {
+			runMap, err := RunMap(baseDir, metric, m)
+			if err != nil {
+				return nil, err
+			}
+
+			runMap["metricId"] = metric
+			data = append(data, runMap)
+		}
+	}
+
+	return data, nil
 }
 
-func RunMap(file string, m map[string]interface{}) (data map[string]interface{}, err error) {
+func RunMap(baseDir string, metric string, m map[string]interface{}) (data map[string]interface{}, err error) {
 	var (
 		ok bool
 	)
 
+	// Create paths for bundle directory and utility functions file
+	bundle := fmt.Sprintf("%s/policies/bundles/%s/", baseDir, metric)
+	operators := fmt.Sprintf("%s/policies/operators.rego", baseDir)
+
 	ctx := context.TODO()
 	r, err := rego.New(
 		rego.Query("data.clouditor"),
-		rego.Load([]string{file}, nil),
+		rego.Load(
+			[]string{
+				bundle + "metric.rego",
+				bundle + "data.json",
+				operators,
+			},
+			nil),
 	).PrepareForEval(ctx)
 	if err != nil {
 		return nil, fmt.Errorf("could not prepare rego evaluation: %w", err)
@@ -61,7 +127,31 @@ func RunMap(file string, m map[string]interface{}) (data map[string]interface{},
 
 	if data, ok = results[0].Expressions[0].Value.(map[string]interface{}); !ok {
 		return nil, errors.New("expected data is not a map[string]interface{}")
+	} else if data["applicable"] == false {
+		return nil, nil
+	} else {
+		return data, nil
 	}
+}
+
+func scanBundleDir(baseDir string) ([]os.FileInfo, error) {
+	dirname := baseDir + "/policies/bundles"
+
+	f, err := os.Open(dirname)
+	if err != nil {
+		return nil, err
+	}
+	files, err := f.Readdir(-1)
+	_ = f.Close()
+	if err != nil {
+		return nil, err
+	}
+
+	return files, err
+}
 
+func createKey(types []string) (key string) {
+	key = strings.Join(types, "-")
+	key = strings.ReplaceAll(key, " ", "")
 	return
 }
diff --git a/proto/assessment.proto b/proto/assessment.proto
index 437898a0..3aefcc22 100644
--- a/proto/assessment.proto
+++ b/proto/assessment.proto
@@ -30,23 +30,25 @@ package clouditor;
 
 import "google/api/annotations.proto";
 import "google/protobuf/empty.proto";
+import "google/protobuf/struct.proto";
+import "google/protobuf/timestamp.proto";
 import "evidence.proto";
 
 option go_package = "api/assessment";
 
-// Representing the link between orchestrator and discovery: Assessing evidences from discovery and sending results
-// to orchestrator
+// Representing the link between orchestrator and discovery: Assessing evidences
+// from discovery and sending results to orchestrator
 service Assessment {
   // Triggers the assessment. Part of the private API,
   // not exposed as REST.
   rpc TriggerAssessment(TriggerAssessmentRequest)
       returns (google.protobuf.Empty) {}
 
-  // TODO(all): Part of public API because external entities (mainly discoveries) can use it?
-  // List the latest set of assessment results. Part of the public API,
-  // also exposed as REST
+  // TODO(all): Part of public API because external entities (mainly
+  // discoveries) can use it? List the latest set of assessment results. Part of
+  // the public API, also exposed as REST
   rpc ListAssessmentResults(ListAssessmentResultsRequest)
-  returns (ListAssessmentResultsResponse) {
+      returns (ListAssessmentResultsResponse) {
     option (google.api.http) = {
       post : "/v1/assessment/results"
       response_body : "*"
@@ -62,8 +64,8 @@ service Assessment {
     };
   }
 
-  // Assesses stream of evidences coming from the discovery. Part of the public API,
-  // not exposed as REST
+  // Assesses stream of evidences coming from the discovery. Part of the public
+  // API, not exposed as REST
   rpc AssessEvidences(stream Evidence) returns (google.protobuf.Empty);
 };
 
@@ -72,15 +74,39 @@ message TriggerAssessmentRequest { string someOption = 1; }
 message ListAssessmentResultsRequest {}
 message ListAssessmentResultsResponse { repeated Result results = 1; }
 
-message AssessEvidenceRequest {Evidence evidence = 1; }
+message AssessEvidenceRequest { Evidence evidence = 1; }
 message AssessEvidenceResponse { bool status = 1; }
 
-// A result resource, representing the result after assessing the cloud resource with id resource_id.
+// A result resource, representing the result after assessing the cloud resource
+// with id resource_id.
 message Result {
-  string resource_id = 1;
+  // Assessment result id
+  string id = 1;
+
+  // Time of assessment
+  google.protobuf.Timestamp timestamp = 2;
+
+  // Reference to the metric the assessment was based on
+  string metric_id = 3;
+
+  // Data corresponding to the metric by the given metric id
+  MetricData metric_data = 4;
+
+  // Compliant case: true or false
+  bool compliant = 5;
+
+  // Reference to the assessed evidence
+  string evidence_id = 6;
 
-  bool compliant = 2;
+  // Reference to the resource of the assessed evidence
+  string resource_id = 7;
 
-  int32 metric_id = 3;
+  // Some comments on the reason for non-compliance
+  string non_compliance_comments = 8;
 }
 
+message MetricData {
+  google.protobuf.Value target_value =
+      1;               // target value, e.g. string, number, array, ...
+  string operator = 2; // the operator, e.g. "=="
+}
\ No newline at end of file
diff --git a/proto/auth.proto b/proto/auth.proto
index 1fd9ce13..43d6a450 100644
--- a/proto/auth.proto
+++ b/proto/auth.proto
@@ -33,14 +33,16 @@ import "google/api/annotations.proto";
 option go_package = "api/auth";
 
 service Authentication {
-  rpc Login(LoginRequest) returns(LoginResponse) {
-    option(google.api.http) = {
-      post : "/v1/auth/login" body : "*" response_body : "*"
+  rpc Login(LoginRequest) returns (LoginResponse) {
+    option (google.api.http) = {
+      post : "/v1/auth/login"
+      body : "*"
+      response_body : "*"
     };
   };
 }
 
-/* A clouditor user */
+// A clouditor user
 message User {
   string username = 1;
   string password = 2;
diff --git a/proto/discovery.proto b/proto/discovery.proto
index de185357..5ed33fd6 100644
--- a/proto/discovery.proto
+++ b/proto/discovery.proto
@@ -33,7 +33,8 @@ import "google/protobuf/struct.proto";
 
 option go_package = "api/discovery";
 
-// Discovers cloud resources and maps these properties to the format of the ontology
+// Discovers cloud resources and maps these properties to the format of the
+// ontology
 service Discovery {
   // Starts discovering the cloud resources
   rpc Start(StartDiscoveryRequest) returns (StartDiscoveryResponse) {
diff --git a/proto/evidence.proto b/proto/evidence.proto
index 5d9291ec..f1f25856 100644
--- a/proto/evidence.proto
+++ b/proto/evidence.proto
@@ -40,23 +40,20 @@ message Evidence {
   // the ID in a uuid format
   string id = 1;
 
-  // reference to a service this evidence was gathered from
-  string service_id = 2;
+  // time of evidence creation
+  google.protobuf.Timestamp timestamp = 2;
 
-  // reference to the resource this evidence was gathered from
-  string resource_id = 3;
+  // Reference to a service this evidence was gathered from
+  string service_id = 3;
 
-  // TODO: replace with google/type/date.proto timestamp.proto or date.proto?
-  google.protobuf.Timestamp timestamp = 4;
+  // Reference to the tool which provided the evidence
+  string tool_id = 4;
 
-  repeated int32 applicable_metrics = 5;
+  // Contains the evidence in its original form without following a defined
+  // schema, e.g. the raw JSON
+  string raw = 5;
 
-  // "raw" evidence (for the auditor), for example the raw JSON response from
-  // the API. This does not follow a defined schema
-  string raw = 6;
-
-  // optional; a semantic representation of the Cloud resource according to our
-  // defined ontology. a JSON serialized node of our semantic graph. This may be
-  // Clouditor-specific.
-  google.protobuf.Value resource = 7;
-}
+  // Semantic representation of the Cloud resource according to our defined
+  // ontology
+  google.protobuf.Value resource = 6;
+}
\ No newline at end of file
diff --git a/proto/metric.proto b/proto/metric.proto
index 46e527db..5a35c57d 100644
--- a/proto/metric.proto
+++ b/proto/metric.proto
@@ -37,7 +37,7 @@ option go_package = "api/assessment";
 // A metric resource
 message Metric {
   // Required. The unique identifier of the metric.
-  int32 id = 1;
+  string id = 1;
 
   // Required. The human readable name of the metric.
   string name = 2;
diff --git a/proto/orchestrator.proto b/proto/orchestrator.proto
index 62479e5c..101e004c 100644
--- a/proto/orchestrator.proto
+++ b/proto/orchestrator.proto
@@ -129,7 +129,7 @@ message DeregisterAssessmentToolRequest { string tool_id = 1; }
 message StoreAssessmentResultRequest { AssessmentResult result = 1; }
 message StoreAssessmentResultResponse {}
 
-message GetMetricsRequest { int32 metric_id = 1; }
+message GetMetricsRequest { string metric_id = 1; }
 
 message ListMetricsRequest {}
 message ListMetricsResponse { repeated Metric metrics = 1; }
diff --git a/service/assessment/assessment.go b/service/assessment/assessment.go
index 695d6166..379b773b 100644
--- a/service/assessment/assessment.go
+++ b/service/assessment/assessment.go
@@ -29,26 +29,28 @@ import (
 	"context"
 	"fmt"
 	"io"
-	"os"
 
 	"clouditor.io/clouditor/api/assessment"
 	"clouditor.io/clouditor/api/evidence"
 	"clouditor.io/clouditor/policies"
+	"github.com/google/uuid"
 	"github.com/sirupsen/logrus"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
 	"google.golang.org/protobuf/types/known/emptypb"
+	"google.golang.org/protobuf/types/known/structpb"
+	"google.golang.org/protobuf/types/known/timestamppb"
 )
 
 var log *logrus.Entry
 
-var listId int32
-
 func init() {
 	log = logrus.WithField("component", "assessment")
 }
 
 type Service struct {
+	// ResultHook is a hook function that can be used if one wants to be
+	// informed about each assessment result
 	ResultHook func(result *assessment.Result, err error)
 
 	results map[string]*assessment.Result
@@ -62,7 +64,7 @@ func NewService() assessment.AssessmentServer {
 }
 
 func (s Service) AssessEvidence(_ context.Context, req *assessment.AssessEvidenceRequest) (res *assessment.AssessEvidenceResponse, err error) {
-	_, err = s.handleEvidence(req.Evidence)
+	err = s.handleEvidence(req.Evidence)
 
 	if err != nil {
 		res = &assessment.AssessEvidenceResponse{
@@ -80,14 +82,14 @@ func (s Service) AssessEvidence(_ context.Context, req *assessment.AssessEvidenc
 }
 
 func (s Service) AssessEvidences(stream assessment.Assessment_AssessEvidencesServer) error {
-	var evidence *evidence.Evidence
+	var e *evidence.Evidence
 	var err error
 
 	for {
-		evidence, err = stream.Recv()
+		e, err = stream.Recv()
 
 		// TODO: Catch error?
-		_, _ = s.handleEvidence(evidence)
+		_ = s.handleEvidence(e)
 
 		if err == io.EOF {
 			log.Infof("Stopped receiving streamed evidences")
@@ -98,60 +100,61 @@ func (s Service) AssessEvidences(stream assessment.Assessment_AssessEvidencesSer
 	}
 }
 
-func (s Service) handleEvidence(evidence *evidence.Evidence) (result *evidence.Evidence, err error) {
-	log.Infof("Received evidence for resource %s", evidence.ResourceId)
-	log.Debugf("Evidence: %+v", evidence)
-
-	var file string
-
-	listId++
-	evidence.Id = fmt.Sprintf("%d", listId)
-
-	if len(evidence.ApplicableMetrics) == 0 {
-		log.Warnf("Could not find a valid metric for evidence of resource %s", evidence.ResourceId)
+func (s Service) handleEvidence(evidence *evidence.Evidence) error {
+	resourceId, err := evidence.Validate()
+	if err != nil {
+		return fmt.Errorf("invalid evidence: %w", err)
 	}
 
-	// TODO(oxisto): actually look up metric via orchestrator
-	for _, metric := range evidence.ApplicableMetrics {
-		var baseDir string = "."
+	log.Infof("Running evidence %s (%s) collected by %s at %v", evidence.Id, resourceId, evidence.ToolId, evidence.Timestamp)
+	log.Debugf("Evidence: %+v", evidence)
+
+	evaluations, err := policies.RunEvidence(evidence)
+	if err != nil {
+		log.Errorf("Could not evaluate evidence: %v", err)
 
-		// check, if we are in the root of Clouditor
-		if _, err := os.Stat("policies"); os.IsNotExist(err) {
-			// in tests, we are relative to our current package
-			baseDir = "../../"
+		// Inform our hook, if we have any
+		if s.ResultHook != nil {
+			go s.ResultHook(nil, err)
 		}
 
-		file = fmt.Sprintf("%s/policies/metric%d.rego", baseDir, metric)
-
-		// TODO(oxisto): use go embed
-		data, err := policies.RunEvidence(file, evidence)
-		if err != nil {
-			log.Errorf("Could not evaluate evidence: %v", err)
+		return err
+	}
 
-			if s.ResultHook != nil {
-				go s.ResultHook(nil, err)
-			}
+	for i, data := range evaluations {
+		metricId := data["metricId"].(string)
 
-			return nil, err
-		}
+		log.Infof("Evaluated evidence with metric '%v' as %v", metricId, data["compliant"])
 
-		log.Infof("Evaluated evidence as %v", data["compliant"])
+		// Get output values of Rego evaluation. If they are not given, the zero value is used
+		operator, _ := data["operator"].(string)
+		targetValue, _ := data["target_value"].(*structpb.Value)
+		compliant, _ := data["compliant"].(bool)
 
 		result := &assessment.Result{
-			ResourceId: evidence.ResourceId,
-			Compliant:  data["compliant"].(bool),
-			MetricId:   int32(metric),
+			Id:        uuid.NewString(),
+			Timestamp: timestamppb.Now(),
+			MetricId:  metricId,
+			MetricData: &assessment.MetricData{
+				TargetValue: targetValue,
+				Operator:    operator,
+			},
+			Compliant:             compliant,
+			EvidenceId:            evidence.Id,
+			ResourceId:            resourceId,
+			NonComplianceComments: "No comments so far",
 		}
 
-		// just a little hack to quickly enable multiple results per resource
-		s.results[fmt.Sprintf("%s-%d", evidence.ResourceId, metric)] = result
+		// Just a little hack to quickly enable multiple results per resource
+		s.results[fmt.Sprintf("%s-%d", resourceId, i)] = result
 
+		// Inform our hook, if we have any
 		if s.ResultHook != nil {
 			go s.ResultHook(result, nil)
 		}
 	}
 
-	return
+	return nil
 }
 
 func (s Service) ListAssessmentResults(_ context.Context, _ *assessment.ListAssessmentResultsRequest) (res *assessment.ListAssessmentResultsResponse, err error) {
diff --git a/service/discovery/aws/compute.go b/service/discovery/aws/compute.go
index 297b6f89..554cb2c7 100644
--- a/service/discovery/aws/compute.go
+++ b/service/discovery/aws/compute.go
@@ -85,6 +85,7 @@ func (d computeDiscovery) Name() string {
 
 // List is the method implementation defined in the discovery.Discoverer interface
 func (d computeDiscovery) List() (resources []voc.IsCloudResource, err error) {
+	log.Infof("Collecting evidences in %s", d.Name())
 	listOfVMs, err := d.discoverVirtualMachines()
 	if err != nil {
 		return nil, fmt.Errorf("could not discover virtual machines: %w", err)
diff --git a/service/discovery/aws/storage.go b/service/discovery/aws/storage.go
index ceb01aa4..fa5b464b 100644
--- a/service/discovery/aws/storage.go
+++ b/service/discovery/aws/storage.go
@@ -85,15 +85,15 @@ type S3API interface {
 
 // BucketPolicy matches the returned bucket policy in JSON from AWS
 type BucketPolicy struct {
-	ID        string
-	Version   string
-	Statement []Statement
+	ID        string      `json:"id"`
+	Version   string      `json:"Version"`
+	Statement []Statement `json:"Statement"`
 }
 type Statement struct {
-	Action   string
-	Effect   string
-	Resource []string
-	Condition
+	Action    interface{} `json:"Action"`
+	Effect    string      `json:"Effect"`
+	Resource  interface{}
+	Condition `json:"Condition"`
 }
 type Condition struct {
 	Bool
@@ -110,17 +110,15 @@ func (d *awsS3Discovery) Name() string {
 // List is the method implementation defined in the discovery.Discoverer interface
 func (d *awsS3Discovery) List() (resources []voc.IsCloudResource, err error) {
 	var encryptionAtRest voc.HasAtRestEncryption
-	var encryptionAtTransmit voc.TransportEncryption
+	var encryptionAtTransmit *voc.TransportEncryption
 
-	log.Info("Starting List() in ", d.Name())
+	log.Infof("Collecting evidences in %s", d.Name())
 	var buckets []bucket
 	buckets, err = d.getBuckets()
 	if err != nil {
 		return
 	}
-	log.Println("Found", len(buckets), "buckets.")
 	for _, b := range buckets {
-		log.Println("Getting resources for", b.name)
 		encryptionAtRest, err = d.getEncryptionAtRest(b)
 		if err != nil {
 			return
@@ -144,7 +142,7 @@ func (d *awsS3Discovery) List() (resources []voc.IsCloudResource, err error) {
 			},
 			HttpEndpoint: &voc.HttpEndpoint{
 				Url:                 b.endpoint,
-				TransportEncryption: &encryptionAtTransmit,
+				TransportEncryption: encryptionAtTransmit,
 			},
 		})
 	}
@@ -165,7 +163,6 @@ func NewAwsStorageDiscovery(client *Client) discovery.Discoverer {
 
 // getBuckets returns all buckets
 func (d *awsS3Discovery) getBuckets() (buckets []bucket, err error) {
-	log.Println("Getting buckets in s3...")
 	var resp *s3.ListBucketsOutput
 	resp, err = d.storageAPI.ListBuckets(context.TODO(), &s3.ListBucketsInput{})
 	if err != nil {
@@ -181,13 +178,17 @@ func (d *awsS3Discovery) getBuckets() (buckets []bucket, err error) {
 		if err != nil {
 			return
 		}
-		buckets = append(buckets, bucket{
-			arn:          "arn:aws:s3:::" + *b.Name,
-			name:         aws.ToString(b.Name),
-			creationTime: aws.ToTime(b.CreationDate),
-			region:       region,
-			endpoint:     "https://" + aws.ToString(b.Name) + ".s3." + region + ".amazonaws.com",
-		})
+		// Currently only buckets are retrieved that are in the region of the users specified region in the config. Since getBucketPolicy throws error if bucket region differs
+		// TODO(lebogg): Retrieve all buckets (just remove if) and fix issues with other methods, e.g. getBucketPolicy
+		if region == d.awsConfig.cfg.Region {
+			buckets = append(buckets, bucket{
+				arn:          "arn:aws:s3:::" + *b.Name,
+				name:         aws.ToString(b.Name),
+				creationTime: aws.ToTime(b.CreationDate),
+				region:       region,
+				endpoint:     "https://" + aws.ToString(b.Name) + ".s3." + region + ".amazonaws.com",
+			})
+		}
 	}
 	return
 }
@@ -243,18 +244,17 @@ func (d *awsS3Discovery) getEncryptionAtRest(bucket bucket) (e voc.HasAtRestEncr
 // "confirm that your bucket policies explicitly deny access to HTTP requests"
 // https://aws.amazon.com/premiumsupport/knowledge-center/s3-bucket-policy-for-config-rule/
 // getTransportEncryption loops over all statements in the bucket policy and checks if one statement denies https only == false
-func (d *awsS3Discovery) getTransportEncryption(bucket string) (encryptionAtTransit voc.TransportEncryption, err error) {
+func (d *awsS3Discovery) getTransportEncryption(bucket string) (*voc.TransportEncryption, error) {
 	input := s3.GetBucketPolicyInput{
 		Bucket:              aws.String(bucket),
 		ExpectedBucketOwner: nil,
 	}
 	var resp *s3.GetBucketPolicyOutput
+	var err error
 
 	resp, err = d.storageAPI.GetBucketPolicy(context.TODO(), &input)
 
 	// encryption at transit (https) is always enabled and TLS version fixed
-	encryptionAtTransit.Enabled = true
-	encryptionAtTransit.TlsVersion = "TLS1.2"
 
 	// Case 1: No bucket policy in place or api error -> 'https only' is not set
 	if err != nil {
@@ -262,15 +262,18 @@ func (d *awsS3Discovery) getTransportEncryption(bucket string) (encryptionAtTran
 		if errors.As(err, &ae) {
 			if ae.ErrorCode() == "NoSuchBucketPolicy" {
 				// This error code is equivalent to "encryption not enforced": set err to nil
-				encryptionAtTransit.Enforced = false
-				err = nil
-				return
+				return &voc.TransportEncryption{
+					Enforced:   false,
+					Enabled:    true,
+					TlsVersion: "TLS1.2",
+					Algorithm:  "TLS",
+				}, nil
 			}
 			// Any other error is a connection error with AWS : Format err and return it
 			err = formatError(ae)
 		}
 		// return any error (but according to doc: "All service API response errors implement the smithy.APIError")
-		return
+		return nil, err
 	}
 
 	// Case 2: bucket policy -> check if https only is set
@@ -278,17 +281,39 @@ func (d *awsS3Discovery) getTransportEncryption(bucket string) (encryptionAtTran
 	var policy BucketPolicy
 	err = json.Unmarshal([]byte(aws.ToString(resp.Policy)), &policy)
 	if err != nil {
-		err = fmt.Errorf("error occurred while unmarshalling the bucket policy:%v", err)
-		return
+		return nil, fmt.Errorf("error occurred while unmarshalling the bucket policy: %v", err)
 	}
 	// one statement has set https only -> default encryption is set
 	for _, statement := range policy.Statement {
-		if statement.Effect == "Deny" && !statement.Condition.AwsSecureTransport && statement.Action == "s3:*" {
-			encryptionAtTransit.Enforced = true
-			return
+		if a, ok := statement.Action.(string); ok {
+			if statement.Effect == "Deny" && !statement.Condition.AwsSecureTransport && a == "s3:*" {
+				return &voc.TransportEncryption{
+					Enforced:   true,
+					Enabled:    true,
+					TlsVersion: "TLS1.2",
+					Algorithm:  "TLS",
+				}, nil
+			}
+		}
+		if actions, ok := statement.Action.([]string); ok {
+			for _, a := range actions {
+				if statement.Effect == "Deny" && !statement.Condition.AwsSecureTransport && a == "s3:*" {
+					return &voc.TransportEncryption{
+						Enforced:   true,
+						Enabled:    true,
+						TlsVersion: "TLS1.2",
+						Algorithm:  "TLS",
+					}, nil
+				}
+			}
 		}
 	}
-	return
+	return &voc.TransportEncryption{
+		Enforced:   false,
+		Enabled:    true,
+		TlsVersion: "TLS1.2",
+		Algorithm:  "TLS",
+	}, nil
 
 }
 
diff --git a/service/discovery/azure/compute.go b/service/discovery/azure/compute.go
index aae059fa..a7937abe 100644
--- a/service/discovery/azure/compute.go
+++ b/service/discovery/azure/compute.go
@@ -163,8 +163,12 @@ func (d *azureComputeDiscovery) handleVirtualMachines(vm *compute.VirtualMachine
 					Region: *vm.Location,
 				},
 			}},
-		BootLog: &voc.BootLog{Log: &voc.Log{Enabled: IsBootDiagnosticEnabled(vm)}},
-		OSLog:   &voc.OSLog{}, // TODO(garuppel): Add OSLog
+		BootLog: &voc.BootLog{Log: &voc.Log{
+			Enabled:         IsBootDiagnosticEnabled(vm),
+			Output:          []voc.ResourceID{voc.ResourceID(*vm.DiagnosticsProfile.BootDiagnostics.StorageURI)},
+			RetentionPeriod: 0, // Currently, configuring the retention period for Managed Boot Diagnostics is not available. The logs will be overwritten after 1gb of space according to https://github.com/MicrosoftDocs/azure-docs/issues/69953
+		}},
+		OSLog: &voc.OSLog{}, // TODO(garuppel): Add OSLog
 		// TODO(garuppel) Add block storage resourceID
 		BlockStorage: []voc.ResourceID{},
 	}
diff --git a/service/discovery/azure/iac_template.go b/service/discovery/azure/iac_template.go
index d9461f64..cf49d26e 100644
--- a/service/discovery/azure/iac_template.go
+++ b/service/discovery/azure/iac_template.go
@@ -450,6 +450,7 @@ func (d *azureIacTemplateDiscovery) handleVirtualMachine(resourceValue map[strin
 	var id string
 	var name string
 	var bootDiagnosticsEnabled bool
+	var storageUri string
 	var properties map[string]interface{}
 	var ok bool
 
@@ -471,6 +472,7 @@ func (d *azureIacTemplateDiscovery) handleVirtualMachine(resourceValue map[strin
 			for propertiesKey, propertiesValue := range properties {
 				if propertiesKey == "diagnosticsProfile" {
 					bootDiagnosticsEnabled = propertiesValue.(map[string]interface{})["bootDiagnostics"].(map[string]interface{})["enabled"].(bool)
+					storageUri = getStorageUriFromArmTemplate(propertiesValue.(map[string]interface{})["bootDiagnostics"].(map[string]interface{})["storageUri"].(string))
 				}
 			}
 		}
@@ -494,7 +496,9 @@ func (d *azureIacTemplateDiscovery) handleVirtualMachine(resourceValue map[strin
 		},
 		BootLog: &voc.BootLog{
 			Log: &voc.Log{
-				Enabled: bootDiagnosticsEnabled,
+				Enabled:         bootDiagnosticsEnabled,
+				Output:          []voc.ResourceID{voc.ResourceID(storageUri)},
+				RetentionPeriod: 0, // Currently, configuring the retention period for Managed Boot Diagnostics is not available. The logs will be overwritten after 1gb of space according to https://github.com/MicrosoftDocs/azure-docs/issues/69953
 			},
 		},
 		OSLog: &voc.OSLog{
@@ -545,3 +549,14 @@ func getContainerName(name string) string {
 
 	return anotherNameSplit[1]
 }
+
+func getStorageUriFromArmTemplate(name string) string {
+	var storageUri string
+
+	// "[concat('https://', parameters('storageAccounts_bcloudtest_name'), '.blob.core.windows.net/')]"
+	nameSplit := strings.Split(name, "'")
+	storageUriName := strings.Split(nameSplit[3], "_")
+	storageUri = nameSplit[1] + storageUriName[1] + nameSplit[5]
+
+	return storageUri
+}
diff --git a/service/discovery/azure/storage.go b/service/discovery/azure/storage.go
index 7dcfefa3..585f7524 100644
--- a/service/discovery/azure/storage.go
+++ b/service/discovery/azure/storage.go
@@ -26,10 +26,11 @@
 package azure
 
 import (
-	"clouditor.io/clouditor/api/discovery"
-	"clouditor.io/clouditor/voc"
 	"context"
 	"fmt"
+
+	"clouditor.io/clouditor/api/discovery"
+	"clouditor.io/clouditor/voc"
 	"github.com/Azure/azure-sdk-for-go/profiles/2020-09-01/compute/mgmt/compute"
 	"github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2021-02-01/storage"
 	"github.com/Azure/go-autorest/autorest/to"
@@ -221,9 +222,9 @@ func handleObjectStorage(account *storage.Account, container storage.ListContain
 			Url: to.String(account.PrimaryEndpoints.Blob) + to.String(container.Name),
 			TransportEncryption: &voc.TransportEncryption{
 				Enforced:   to.Bool(account.EnableHTTPSTrafficOnly),
-				Enabled:    true, // TODO get from IaC template in all storages
+				Enabled:    true, // TODO(garuppel): can we get that from the API
 				TlsVersion: string(account.MinimumTLSVersion),
-				Algorithm:  "", // not available
+				Algorithm:  "TLS",
 			},
 		},
 	}
@@ -251,22 +252,19 @@ func handleFileStorage(account *storage.Account, fileshare storage.FileShareItem
 				Enforced:   to.Bool(account.EnableHTTPSTrafficOnly),
 				Enabled:    true, // cannot be disabled
 				TlsVersion: string(account.MinimumTLSVersion),
-				Algorithm:  "", // not available
+				Algorithm:  "AES256", // TODO(garuppel): I changed it due to https://docs.microsoft.com/en-us/azure/storage/common/storage-service-encryption
 			},
 		},
 	}
 }
 
-func getBlockStorageAtRestEncryption(disk compute.Disk) voc.HasAtRestEncryption {
-
-	var enc voc.HasAtRestEncryption
-
+func getBlockStorageAtRestEncryption(disk compute.Disk) (enc voc.HasAtRestEncryption) {
 	if disk.Encryption.Type == compute.EncryptionAtRestWithPlatformKey {
 		enc = voc.ManagedKeyEncryption{AtRestEncryption: &voc.AtRestEncryption{
-			Algorithm: "", // not available
+			Algorithm: "AES256", // See https://docs.microsoft.com/en-us/azure/storage/common/storage-service-encryption
 			Enabled:   true,
 		}}
-	} else if disk.Encryption.Type == compute.EncryptionAtRestWithCustomerKey && *disk.EncryptionSettingsCollection.Enabled {
+	} else if disk.Encryption.Type == compute.EncryptionAtRestWithCustomerKey {
 		var keyUrl string
 		for _, elem := range *disk.EncryptionSettingsCollection.EncryptionSettings {
 			keyUrl = *elem.DiskEncryptionKey.SourceVault.ID
diff --git a/service/discovery/discovery.go b/service/discovery/discovery.go
index 0ab09766..d0cf1bf2 100644
--- a/service/discovery/discovery.go
+++ b/service/discovery/discovery.go
@@ -30,6 +30,10 @@ import (
 	"strings"
 	"time"
 
+	"clouditor.io/clouditor/service/discovery/azure"
+	"clouditor.io/clouditor/service/discovery/k8s"
+	"google.golang.org/protobuf/types/known/timestamppb"
+
 	"github.com/google/uuid"
 
 	"clouditor.io/clouditor/service/discovery/aws"
@@ -37,8 +41,6 @@ import (
 	"clouditor.io/clouditor/api/assessment"
 	"clouditor.io/clouditor/api/discovery"
 	"clouditor.io/clouditor/api/evidence"
-	"clouditor.io/clouditor/service/discovery/azure"
-	"clouditor.io/clouditor/service/discovery/k8s"
 	"clouditor.io/clouditor/voc"
 	"github.com/Azure/go-autorest/autorest/azure/auth"
 	"github.com/go-co-op/gocron"
@@ -201,17 +203,13 @@ func (s Service) StartDiscovery(discoverer discovery.Discoverer) {
 			log.Errorf("Could not convert resource to protobuf struct: %v", err)
 		}
 
-		evidence := &evidence.Evidence{
-			Id:         uuid.New().String(),
-			Resource:   v,
-			ResourceId: string(resource.GetID()),
-		}
-
-		// check for object storage
-		for _, v := range resource.GetType() {
-			if v == "ObjectStorage" {
-				evidence.ApplicableMetrics = []int32{1, 2}
-			}
+		// TODO(all): What is the raw type in our case?
+		e := &evidence.Evidence{
+			Id:        uuid.New().String(),
+			Timestamp: timestamppb.Now(),
+			ToolId:    "Clouditor Evidences Collection",
+			Raw:       "",
+			Resource:  v,
 		}
 
 		if s.AssessmentStream == nil {
@@ -226,12 +224,12 @@ func (s Service) StartDiscovery(discoverer discovery.Discoverer) {
 
 		log.Debugf("Sending evidence for resource %s (%s)...", resource.GetID(), strings.Join(resource.GetType(), ", "))
 
-		err = s.AssessmentStream.Send(evidence)
+		err = s.AssessmentStream.Send(e)
 		if err != nil {
 			log.Errorf("Could not send evidence to Assessment: %v", err)
 		}
 
-		err = s.EvidenceStoreStream.Send(evidence)
+		err = s.EvidenceStoreStream.Send(e)
 		if err != nil {
 			log.Errorf("Could not send evidence to EvidenceStore: %v", err)
 		}
diff --git a/service/discovery/k8s/network.go b/service/discovery/k8s/network.go
index 73e571e9..3a8968b7 100644
--- a/service/discovery/k8s/network.go
+++ b/service/discovery/k8s/network.go
@@ -56,7 +56,7 @@ func (k k8sNetworkDiscovery) List() ([]voc.IsCloudResource, error) {
 
 	services, err := k.intf.CoreV1().Services("").List(context.TODO(), metav1.ListOptions{})
 	if err != nil {
-		return nil, fmt.Errorf("could not list services: %v", err)
+		return nil, fmt.Errorf("could not list services: %w", err)
 	}
 
 	for i := range services.Items {
@@ -70,7 +70,7 @@ func (k k8sNetworkDiscovery) List() ([]voc.IsCloudResource, error) {
 	// TODO Does not get ingresses
 	ingresses, err := k.intf.NetworkingV1().Ingresses("").List(context.TODO(), metav1.ListOptions{})
 	if err != nil {
-		return list, fmt.Errorf("could not list ingresses: %v", err)
+		return list, fmt.Errorf("could not list ingresses: %w", err)
 	}
 
 	for i := range ingresses.Items {
diff --git a/service/evidence/evidence_store.go b/service/evidence/evidence_store.go
index fbdd9777..eaf29225 100644
--- a/service/evidence/evidence_store.go
+++ b/service/evidence/evidence_store.go
@@ -6,6 +6,8 @@ import (
 
 	"clouditor.io/clouditor/api/evidence"
 	"github.com/sirupsen/logrus"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
 	"google.golang.org/protobuf/types/known/emptypb"
 )
 
@@ -31,10 +33,15 @@ func init() {
 // StoreEvidence is a method implementation of the evidenceServer interface: It receives an evidence and stores it
 func (s *Service) StoreEvidence(_ context.Context, e *evidence.Evidence) (*evidence.StoreEvidenceResponse, error) {
 	var (
-		resp       = &evidence.StoreEvidenceResponse{}
-		err  error = nil
+		resp = &evidence.StoreEvidenceResponse{}
+		err  error
 	)
 
+	_, err = e.Validate()
+	if err != nil {
+		return nil, status.Errorf(codes.InvalidArgument, "invalid evidence: %v", err)
+	}
+
 	s.evidences[e.Id] = e
 	resp.Status = true
 	return resp, err
diff --git a/service/orchestrator/metrics.json b/service/orchestrator/metrics.json
index 62eaab58..35db15d1 100644
--- a/service/orchestrator/metrics.json
+++ b/service/orchestrator/metrics.json
@@ -1,7 +1,101 @@
 [
   {
-    "id": 1,
-    "name": "Transport Encryption",
+    "id": "AnomalyDetectionEnabled"
+  },
+  {
+    "id": "AnomalyDetectionOutput"
+  },
+  {
+    "id": "AnonymousAuthentication"
+  },
+  {
+    "id": "AtRestEncryptionEnabled",
+    "name": "Storage Encryption: Enabled",
+    "description": "This metric describes whether storage encryption is enabled",
+    "scale": 1,
+    "allowedValues": {
+      "values": [
+        false,
+        true
+      ]
+    }
+  },
+  {
+    "id": "AtRestEncryptionAlgorithm",
+    "name": "Storage Encryption: Algorithm",
+    "description": "This metric describes the algorithm of a storage encryption block cipher",
+    "scale": 1,
+    "allowedValues": {
+      "values": [
+        "AES-128",
+        "AES-256",
+        ""
+      ]
+    }
+  },
+  {
+    "id": "AutomaticUpdatesEnabled"
+  },
+  {
+    "id": "AutomaticUpdatesInterval"
+  },
+  {
+    "id": "AutomaticUpdatesSecurityOnly"
+  },
+  {
+    "id": "BootLoggingEnabled"
+  },
+  {
+    "id": "BootLoggingOutput"
+  },
+  {
+    "id": "BootLoggingRetention"
+  },
+  {
+    "id": "BroadAssignments"
+  },
+  {
+    "id": "CustomerKeyEncryption"
+  },
+  {
+    "id": "L3FirewallEnabled"
+  },
+  {
+    "id": "L3FirewallRestrictedPorts"
+  },
+  {
+    "id": "MalwareProtectionEnabled"
+  },
+  {
+    "id": "MalwareProtectionOutput"
+  },
+  {
+    "id": "MixedDuties"
+  },
+  {
+    "id": "MutualAuthentication"
+  },
+  {
+    "id": "OSLoggingEnabled"
+  },
+  {
+    "id": "OSLoggingOutput"
+  },
+  {
+    "id": "OSLoggingRetention"
+  },
+  {
+    "id": "RuntimeVersion"
+  },
+  {
+    "id": "SingleSignOnEnabled"
+  },
+  {
+    "id": "TLSVersion"
+  },
+  {
+    "id": "TransportEncryptionEnabled",
+    "name": "Transport Encryption: Enabled",
     "description": "This metric describes, whether transport encryption is turned on or not",
     "scale": 1,
     "range": {
@@ -11,11 +105,14 @@
           true
         ]
       }
-    }
+    },
+    "requirements_id": [
+      "CKM-02.2"
+    ]
   },
   {
-    "id": 2,
-    "name": "Transport Encryption Protocol Version",
+    "id": "TransportEncryptionAlgorithm",
+    "name": "Transport Encryption: Protocol Version",
     "description": "This metric describes, whether a up-to-date transport encryption protocol version is used",
     "scale": 1,
     "range": {
@@ -27,18 +124,12 @@
           "TLS 1.3"
         ]
       }
-    }
+    },
+    "requirements_id": [
+      "CKM-02.2"
+    ]
   },
   {
-    "id": 3,
-    "name": "Storage Encryption Key Size",
-    "description": "This metric describes the key size of a storage encryption block cipher",
-    "scale": 1,
-    "range": {
-      "minMax": {
-        "min": 128,
-        "max": 256
-      }
-    }
+    "id": "WebApplicationFirewallEnabled"
   }
 ]
\ No newline at end of file
diff --git a/service/orchestrator/orchestrator.go b/service/orchestrator/orchestrator.go
index 194bc731..40a162b0 100644
--- a/service/orchestrator/orchestrator.go
+++ b/service/orchestrator/orchestrator.go
@@ -42,7 +42,7 @@ import (
 var f embed.FS
 
 var metrics []*assessment.Metric
-var metricIndex map[int32]*assessment.Metric
+var metricIndex map[string]*assessment.Metric
 var log *logrus.Entry
 
 var DefaultMetricsFile = "metrics.json"
@@ -59,7 +59,7 @@ func init() {
 		log.Errorf("Could not load embedded metrics. Will continue with empty metric list: %v", err)
 	}
 
-	metricIndex = make(map[int32]*assessment.Metric)
+	metricIndex = make(map[string]*assessment.Metric)
 	for _, v := range metrics {
 		metricIndex[v.Id] = v
 	}
@@ -97,7 +97,7 @@ func (*Service) GetMetric(_ context.Context, request *orchestrator.GetMetricsReq
 	var metric *assessment.Metric
 
 	if metric, ok = metricIndex[request.MetricId]; !ok {
-		return nil, status.Errorf(codes.NotFound, "Could not find metric with id %d", request.MetricId)
+		return nil, status.Errorf(codes.NotFound, "Could not find metric with id %s", request.MetricId)
 	}
 
 	response = metric
diff --git a/service/standalone/assessment.go b/service/standalone/assessment.go
deleted file mode 100644
index b78f99c7..00000000
--- a/service/standalone/assessment.go
+++ /dev/null
@@ -1,142 +0,0 @@
-// Copyright 2021 Fraunhofer AISEC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//           $$\                           $$\ $$\   $$\
-//           $$ |                          $$ |\__|  $$ |
-//  $$$$$$$\ $$ | $$$$$$\  $$\   $$\  $$$$$$$ |$$\ $$$$$$\    $$$$$$\   $$$$$$\
-// $$  _____|$$ |$$  __$$\ $$ |  $$ |$$  __$$ |$$ |\_$$  _|  $$  __$$\ $$  __$$\
-// $$ /      $$ |$$ /  $$ |$$ |  $$ |$$ /  $$ |$$ |  $$ |    $$ /  $$ |$$ | \__|
-// $$ |      $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$\ $$ |  $$ |$$ |
-// \$$$$$$\  $$ |\$$$$$   |\$$$$$   |\$$$$$$  |$$ |  \$$$   |\$$$$$   |$$ |
-//  \_______|\__| \______/  \______/  \_______|\__|   \____/  \______/ \__|
-//
-// This file is part of Clouditor Community Edition.
-
-package standalone
-
-import (
-	"context"
-
-	"clouditor.io/clouditor/api/assessment"
-	"clouditor.io/clouditor/api/evidence"
-	service_assessment "clouditor.io/clouditor/service/assessment"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/metadata"
-	"google.golang.org/protobuf/types/known/emptypb"
-)
-
-var assessmentService assessment.AssessmentServer
-
-type standaloneEvidenceStream struct {
-	serverChannel chan *evidence.Evidence
-	clientChannel chan *emptypb.Empty
-	ctx           context.Context
-}
-
-type standaloneEvidenceClient struct{}
-
-func (s standaloneEvidenceStream) SendAndClose(*emptypb.Empty) error {
-	s.clientChannel <- &emptypb.Empty{}
-
-	return nil
-}
-
-func (s standaloneEvidenceStream) CloseAndRecv() (*emptypb.Empty, error) {
-	empty := <-s.clientChannel
-
-	return empty, nil
-}
-
-func (standaloneEvidenceStream) CloseSend() error {
-	return nil
-}
-
-func (standaloneEvidenceStream) Header() (metadata.MD, error) {
-	return nil, nil
-}
-
-func (standaloneEvidenceStream) Trailer() metadata.MD {
-	return nil
-}
-
-func (standaloneEvidenceStream) Context() context.Context {
-	return nil
-}
-
-func (s standaloneEvidenceStream) Send(evidence *evidence.Evidence) error {
-	s.serverChannel <- evidence
-
-	return nil
-}
-
-func (standaloneEvidenceStream) SendHeader(metadata.MD) error {
-	return nil
-}
-
-func (standaloneEvidenceStream) SetHeader(metadata.MD) error {
-	return nil
-}
-
-func (standaloneEvidenceStream) SetTrailer(metadata.MD) {
-}
-
-func (s standaloneEvidenceStream) Recv() (*evidence.Evidence, error) {
-	evidence := <-s.serverChannel
-
-	return evidence, nil
-}
-
-func (standaloneEvidenceStream) RecvMsg(_ interface{}) error {
-	return nil
-}
-
-func (standaloneEvidenceStream) SendMsg(_ interface{}) error {
-	return nil
-}
-
-func (standaloneEvidenceClient) TriggerAssessment(ctx context.Context, in *assessment.TriggerAssessmentRequest, _ ...grpc.CallOption) (*emptypb.Empty, error) {
-	return assessmentService.TriggerAssessment(ctx, in)
-}
-
-func (standaloneEvidenceClient) AssessEvidence(ctx context.Context, in *assessment.AssessEvidenceRequest, _ ...grpc.CallOption) (*assessment.AssessEvidenceResponse, error) {
-	return assessmentService.AssessEvidence(ctx, in)
-}
-
-func (standaloneEvidenceClient) ListAssessmentResults(ctx context.Context, in *assessment.ListAssessmentResultsRequest, _ ...grpc.CallOption) (*assessment.ListAssessmentResultsResponse, error) {
-	return assessmentService.ListAssessmentResults(ctx, in)
-}
-
-func (standaloneEvidenceClient) AssessEvidences(_ context.Context, _ ...grpc.CallOption) (assessment.Assessment_AssessEvidencesClient, error) {
-	var stream = &standaloneEvidenceStream{
-		serverChannel: make(chan *evidence.Evidence),
-		clientChannel: make(chan *emptypb.Empty),
-		ctx:           context.Background(),
-	}
-
-	go func() {
-		_ = assessmentService.AssessEvidences(stream)
-	}()
-
-	return stream, nil
-}
-
-func NewAssessmentClient() assessment.AssessmentClient {
-	return &standaloneEvidenceClient{}
-}
-
-func NewAssessmentServer() assessment.AssessmentServer {
-	assessmentService = service_assessment.NewService()
-
-	return assessmentService
-}
diff --git a/service/standalone/standalone.go b/service/standalone/standalone.go
deleted file mode 100644
index 0afef30b..00000000
--- a/service/standalone/standalone.go
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2021 Fraunhofer AISEC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//           $$\                           $$\ $$\   $$\
-//           $$ |                          $$ |\__|  $$ |
-//  $$$$$$$\ $$ | $$$$$$\  $$\   $$\  $$$$$$$ |$$\ $$$$$$\    $$$$$$\   $$$$$$\
-// $$  _____|$$ |$$  __$$\ $$ |  $$ |$$  __$$ |$$ |\_$$  _|  $$  __$$\ $$  __$$\
-// $$ /      $$ |$$ /  $$ |$$ |  $$ |$$ /  $$ |$$ |  $$ |    $$ /  $$ |$$ | \__|
-// $$ |      $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$\ $$ |  $$ |$$ |
-// \$$$$$$\  $$ |\$$$$$   |\$$$$$   |\$$$$$$  |$$ |  \$$$   |\$$$$$   |$$ |
-//  \_______|\__| \______/  \______/  \_______|\__|   \____/  \______/ \__|
-//
-// This file is part of Clouditor Community Edition.
-
-// Package standalone contains utility code for the invididual Clouditor services to be run in a standalone mode.
-package standalone
diff --git a/voc/log.go b/voc/log.go
index 80989244..79543402 100644
--- a/voc/log.go
+++ b/voc/log.go
@@ -29,6 +29,8 @@ import "time"
 
 type Log struct {
 	*Auditing
+	// TODO(all): How to know resource IDs beforehand (coming from metrics)? Maybe sth. like type or ignore it completely?
+	// TODO(all): Is it enough that one item of Output matches one output of data (Rego)?
 	Output          []ResourceID  `json:"output"`
 	Enabled         bool          `json:"enabled"`
 	RetentionPeriod time.Duration `json:"retentionPeriod"`
diff --git a/voc/voc.go b/voc/voc.go
index b46f700d..67a7dfb7 100644
--- a/voc/voc.go
+++ b/voc/voc.go
@@ -96,10 +96,10 @@ func ToStruct(r IsCloudResource) (s *structpb.Value, err error) {
 	// speed increase in marshaling the whole resource list, because
 	// we first need to build it out of the map anyway
 	if b, err = json.Marshal(r); err != nil {
-		return nil, fmt.Errorf("JSON marshal failed: %v", err)
+		return nil, fmt.Errorf("JSON marshal failed: %w", err)
 	}
 	if err = json.Unmarshal(b, &s); err != nil {
-		return nil, fmt.Errorf("JSON unmarshal failed: %v", err)
+		return nil, fmt.Errorf("JSON unmarshal failed: %w", err)
 	}
 
 	return


Test patch:
diff --git a/cli/commands/assessment/assessment_test.go b/cli/commands/assessment/assessment_test.go
deleted file mode 100644
index 330edc29..00000000
--- a/cli/commands/assessment/assessment_test.go
+++ /dev/null
@@ -1,166 +0,0 @@
-// Copyright 2021 Fraunhofer AISEC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//           $$\                           $$\ $$\   $$\
-//           $$ |                          $$ |\__|  $$ |
-//  $$$$$$$\ $$ | $$$$$$\  $$\   $$\  $$$$$$$ |$$\ $$$$$$\    $$$$$$\   $$$$$$\
-// $$  _____|$$ |$$  __$$\ $$ |  $$ |$$  __$$ |$$ |\_$$  _|  $$  __$$\ $$  __$$\
-// $$ /      $$ |$$ /  $$ |$$ |  $$ |$$ /  $$ |$$ |  $$ |    $$ /  $$ |$$ | \__|
-// $$ |      $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$\ $$ |  $$ |$$ |
-// \$$$$$$\  $$ |\$$$$$   |\$$$$$   |\$$$$$$  |$$ |  \$$$   |\$$$$$   |$$ |
-//  \_______|\__| \______/  \______/  \_______|\__|   \____/  \______/ \__|
-//
-// This file is part of Clouditor Community Edition.
-
-package assessment_test
-
-import (
-	"bytes"
-	"context"
-	"fmt"
-	"io/ioutil"
-	"net"
-	"os"
-	"testing"
-	"time"
-
-	"clouditor.io/clouditor/api/assessment"
-	"clouditor.io/clouditor/api/evidence"
-	"clouditor.io/clouditor/cli"
-	cli_assessment "clouditor.io/clouditor/cli/commands/assessment"
-	"clouditor.io/clouditor/cli/commands/login"
-	service_assessment "clouditor.io/clouditor/service/assessment"
-	service_auth "clouditor.io/clouditor/service/auth"
-	"clouditor.io/clouditor/service/standalone"
-	"clouditor.io/clouditor/voc"
-	"github.com/spf13/viper"
-	"github.com/stretchr/testify/assert"
-	"google.golang.org/grpc"
-	"google.golang.org/protobuf/encoding/protojson"
-)
-
-var sock net.Listener
-var server *grpc.Server
-
-func TestMain(m *testing.M) {
-	var (
-		dir string
-		err error
-	)
-
-	// make sure, that we are in the clouditor root folder to find the policies
-	err = os.Chdir("../../..")
-	if err != nil {
-		panic(err)
-	}
-
-	var ready chan bool = make(chan bool)
-
-	sock, server, err = service_auth.StartDedicatedAuthServer(":0")
-	if err != nil {
-		panic(err)
-	}
-
-	defer sock.Close()
-	defer server.Stop()
-
-	assessmentServer := standalone.NewAssessmentServer().(*service_assessment.Service)
-	assessmentServer.ResultHook = func(result *assessment.Result, err error) {
-		ready <- true
-	}
-	assessment.RegisterAssessmentServer(server, assessmentServer)
-
-	client := standalone.NewAssessmentClient()
-
-	resource := &voc.ObjectStorage{
-		Storage: &voc.Storage{
-			CloudResource: &voc.CloudResource{
-				ID:   "some-id",
-				Type: []string{"ObjectStorage", "Storage", "Resource"},
-			},
-		},
-		HttpEndpoint: &voc.HttpEndpoint{
-			TransportEncryption: &voc.TransportEncryption{
-				Enforced:   true,
-				Enabled:    true,
-				TlsVersion: "TLS1_2",
-			},
-		},
-	}
-
-	s, err := voc.ToStruct(resource)
-	if err != nil {
-		panic(err)
-	}
-
-	evidence := &assessment.AssessEvidenceRequest{
-		Evidence: &evidence.Evidence{
-			ResourceId:        "some-id",
-			ApplicableMetrics: []int32{1},
-			Resource:          s,
-		},
-	}
-
-	_, err = client.AssessEvidence(context.Background(), evidence)
-	if err != nil {
-		panic(err)
-	}
-
-	// make the test wait for evidence to be stored
-	select {
-	case <-ready:
-		break
-	case <-time.After(10 * time.Second):
-		panic("Timeout while waiting for evidence assessment result to be ready")
-	}
-
-	// TODO(oxisto): refactor the next lines into a common test login function
-	dir, err = ioutil.TempDir(os.TempDir(), ".clouditor")
-	if err != nil {
-		panic(err)
-	}
-
-	viper.Set("username", "clouditor")
-	viper.Set("password", "clouditor")
-	viper.Set("session-directory", dir)
-
-	cmd := login.NewLoginCommand()
-	err = cmd.RunE(nil, []string{fmt.Sprintf("localhost:%d", sock.Addr().(*net.TCPAddr).Port)})
-	if err != nil {
-		panic(err)
-	}
-
-	// ToDo(lebogg): Check if CRT-D0011 deepsource bug risk warning still occurs
-	defer os.Exit(m.Run())
-}
-
-func TestListResults(t *testing.T) {
-	var b bytes.Buffer
-	var err error
-
-	cli.Output = &b
-
-	cmd := cli_assessment.NewListResultsCommand()
-	err = cmd.RunE(nil, []string{})
-
-	assert.Nil(t, err)
-
-	var response *assessment.ListAssessmentResultsResponse = &assessment.ListAssessmentResultsResponse{}
-
-	err = protojson.Unmarshal(b.Bytes(), response)
-
-	assert.Nil(t, err)
-	assert.NotNil(t, response)
-	assert.NotEmpty(t, response.Results)
-}
diff --git a/cli/commands/metric/metric_test.go b/cli/commands/metric/metric_test.go
index 03dd850f..7023151d 100644
--- a/cli/commands/metric/metric_test.go
+++ b/cli/commands/metric/metric_test.go
@@ -110,7 +110,7 @@ func TestGetMetric(t *testing.T) {
 	cli.Output = &b
 
 	cmd := metric.NewGetMetricCommand()
-	err = cmd.RunE(nil, []string{"1"})
+	err = cmd.RunE(nil, []string{"TransportEncryptionEnabled"})
 
 	assert.Nil(t, err)
 }
diff --git a/policies/policies_test.go b/policies/policies_test.go
index 08b17ba4..feb771fa 100644
--- a/policies/policies_test.go
+++ b/policies/policies_test.go
@@ -26,55 +26,247 @@
 package policies_test
 
 import (
-	"encoding/json"
+	"os"
 	"testing"
 
+	"clouditor.io/clouditor/voc"
+
 	"clouditor.io/clouditor/api/evidence"
 	"clouditor.io/clouditor/policies"
 	"github.com/stretchr/testify/assert"
-	"google.golang.org/protobuf/types/known/structpb"
 )
 
-func TestRun(t *testing.T) {
-	var (
-		m    map[string]interface{}
-		data map[string]interface{}
-		s    *structpb.Struct
-		err  error
-	)
+const (
+	mockObjStorage1EvidenceID = "1"
+	mockObjStorage1ResourceID = "/mockresources/storages/object1"
+	mockObjStorage2EvidenceID = "2"
+	mockObjStorage2ResourceID = "/mockresources/storages/object2"
+	mockVM1EvidenceID         = "3"
+	mockVM1ResourceID         = "/mockresources/compute/vm1"
+	mockVM2EvidenceID         = "4"
+	mockVM2ResourceID         = "/mockresources/compute/vm2"
+)
+
+func TestMain(m *testing.M) {
+	// make sure, that we are in the clouditor root folder to find the policies
+	err := os.Chdir("../")
+	if err != nil {
+		panic(err)
+	}
+
+	os.Exit(m.Run())
+}
 
-	j := `{
-		"atRestEncryption": {
-			"algorithm": "AES-265",
-			"enabled": true,
-			"keyManager": "Microsoft.Storage"
+func TestRunEvidence(t *testing.T) {
+	type fields struct {
+		resource   voc.IsCloudResource
+		evidenceID string
+	}
+	tests := []struct {
+		name       string
+		fields     fields
+		applicable bool
+		compliant  bool
+		wantErr    bool
+	}{
+		{
+			name: "ObjectStorage: Compliant Case",
+			fields: fields{
+				resource: voc.ObjectStorage{
+					Storage: &voc.Storage{
+						CloudResource: &voc.CloudResource{
+							ID:           mockObjStorage1ResourceID,
+							CreationTime: 1621086669,
+							Type:         []string{"ObjectStorage", "Storage", "Resource"},
+							GeoLocation:  voc.GeoLocation{},
+						},
+						AtRestEncryption: &voc.CustomerKeyEncryption{
+							AtRestEncryption: &voc.AtRestEncryption{
+								Algorithm: "AES256",
+								Enabled:   true,
+							},
+							KeyUrl: "SomeUrl",
+						},
+					},
+					HttpEndpoint: &voc.HttpEndpoint{
+						Functionality: nil,
+						Authenticity:  nil,
+						TransportEncryption: &voc.TransportEncryption{
+							Enforced:   true,
+							Enabled:    true,
+							TlsVersion: "TLS1.3",
+							Algorithm:  "TLS",
+						},
+						Url:     "https://aybazestorage.blob.core.windows.net/",
+						Method:  "",
+						Handler: "",
+						Path:    "",
+					},
+				},
+				evidenceID: mockObjStorage1EvidenceID,
+			},
+			applicable: true,
+			compliant:  true,
+			wantErr:    false,
+		}, {
+			name: "ObjectStorage: Non-Compliant Case with no Encryption at rest",
+			fields: fields{
+				resource: voc.ObjectStorage{
+					Storage: &voc.Storage{
+						CloudResource: &voc.CloudResource{
+							ID:           mockObjStorage2ResourceID,
+							CreationTime: 1621086669,
+							Type:         []string{"ObjectStorage", "Storage", "Resource"},
+							GeoLocation:  voc.GeoLocation{},
+						},
+						AtRestEncryption: &voc.AtRestEncryption{
+							Algorithm: "NoGoodAlg",
+							Enabled:   false,
+						},
+					},
+					HttpEndpoint: &voc.HttpEndpoint{
+						Functionality: nil,
+						Authenticity:  nil,
+						TransportEncryption: &voc.TransportEncryption{
+							Enforced:   false,
+							Enabled:    false,
+							TlsVersion: "1.0",
+							Algorithm:  "NoTLS",
+						},
+						Url:     "https://aybazestorage.blob.core.windows.net/",
+						Method:  "",
+						Handler: "",
+						Path:    "",
+					},
+				},
+				evidenceID: mockObjStorage2EvidenceID,
+			},
+			applicable: true,
+			compliant:  false,
+			wantErr:    false,
 		},
-		"creationTime": 1621086669,
-		"httpEndpoint": {
-			"transportEncryption": {
-				"enabled": true,
-				"enforced": true,
-				"tlsVersion": "TLS1_2"
+		{
+			name: "ObjectStorage: Non-Compliant Case 2 with no customer managed key",
+			fields: fields{
+				resource: voc.ObjectStorage{
+					Storage: &voc.Storage{
+						CloudResource: &voc.CloudResource{
+							ID:           mockObjStorage2ResourceID,
+							CreationTime: 1621086669,
+							Type:         []string{"ObjectStorage", "Storage", "Resource"},
+							GeoLocation:  voc.GeoLocation{},
+						},
+						AtRestEncryption: &voc.ManagedKeyEncryption{
+							AtRestEncryption: &voc.AtRestEncryption{
+								// Normally given but for test case purpose only check that no key URL is given
+								Algorithm: "",
+								Enabled:   false,
+							},
+						},
+					},
+					HttpEndpoint: &voc.HttpEndpoint{
+						Functionality: nil,
+						Authenticity:  nil,
+						TransportEncryption: &voc.TransportEncryption{
+							Enforced:   false,
+							Enabled:    false,
+							TlsVersion: "1.0",
+							Algorithm:  "NoTLS",
+						},
+						Url:     "https://aybazestorage.blob.core.windows.net/",
+						Method:  "",
+						Handler: "",
+						Path:    "",
+					},
+				},
+				evidenceID: mockObjStorage2EvidenceID,
 			},
-			"url": "https://aybazestorage.blob.core.windows.net/"
+			applicable: true,
+			compliant:  false,
+			wantErr:    false,
 		},
-		"id": "/subscriptions/e3ed0e96-57bc-4d81-9594-f239540cd77a/resourceGroups/titan/providers/Microsoft.Storage/storageAccounts/aybazestorage",
-		"name": "aybazestorage"
-	}`
-
-	err = json.Unmarshal([]byte(j), &m)
-
-	assert.Nil(t, err)
-
-	s, err = structpb.NewStruct(m)
-
-	assert.Nil(t, err)
-
-	data, err = policies.RunEvidence("metric1.rego", &evidence.Evidence{
-		Resource: structpb.NewStructValue(s),
-	})
-
-	assert.Nil(t, err)
-	assert.NotNil(t, data)
-	assert.Equal(t, true, data["compliant"])
+		{
+			name: "VM:Compliant Case",
+			fields: fields{
+				resource: voc.VirtualMachine{
+					Compute: &voc.Compute{
+						CloudResource: &voc.CloudResource{
+							ID:   mockVM1ResourceID,
+							Type: []string{"Compute", "Virtual Machine", "Resource"},
+						}},
+					NetworkInterface: nil,
+					BlockStorage:     nil,
+					BootLog: &voc.BootLog{
+						Log: &voc.Log{
+							Output:          []voc.ResourceID{"SomeResourceId1", "SomeResourceId2"},
+							Enabled:         true,
+							RetentionPeriod: 36,
+						},
+					},
+					OSLog: &voc.OSLog{
+						Log: &voc.Log{
+							Output:          []voc.ResourceID{"SomeResourceId2"},
+							Enabled:         true,
+							RetentionPeriod: 36,
+						},
+					},
+				},
+				evidenceID: mockVM1EvidenceID,
+			},
+			applicable: true,
+			compliant:  true,
+			wantErr:    false,
+		},
+		{
+			name: "VM:Non-Compliant Case",
+			fields: fields{
+				resource: voc.VirtualMachine{
+					Compute: &voc.Compute{
+						CloudResource: &voc.CloudResource{
+							ID:   mockVM2ResourceID,
+							Type: []string{"Compute", "Virtual Machine", "Resource"},
+						}},
+					NetworkInterface: nil,
+					BlockStorage:     nil,
+					BootLog: &voc.BootLog{
+						Log: &voc.Log{
+							Output:          []voc.ResourceID{},
+							Enabled:         false,
+							RetentionPeriod: 1,
+						},
+					},
+					OSLog: &voc.OSLog{
+						Log: &voc.Log{
+							Output:          []voc.ResourceID{"SomeResourceId3"},
+							Enabled:         false,
+							RetentionPeriod: 1,
+						},
+					},
+				},
+				evidenceID: mockVM2EvidenceID,
+			},
+			applicable: true,
+			compliant:  false,
+			wantErr:    false,
+		},
+	}
+	for _, tt := range tests {
+		resource, err := voc.ToStruct(tt.fields.resource)
+		assert.Nil(t, err)
+		t.Run(tt.name, func(t *testing.T) {
+			results, err := policies.RunEvidence(&evidence.Evidence{
+				Id:       tt.fields.evidenceID,
+				Resource: resource,
+			})
+			if (err != nil) != tt.wantErr {
+				t.Errorf("RunEvidence() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			assert.NotEmpty(t, results)
+			for _, result := range results {
+				assert.Equal(t, tt.applicable, result["applicable"].(bool))
+				assert.Equal(t, tt.compliant, result["compliant"].(bool))
+			}
+		})
+	}
 }
diff --git a/service/assessment/assessment_test.go b/service/assessment/assessment_test.go
new file mode 100644
index 00000000..c570ee55
--- /dev/null
+++ b/service/assessment/assessment_test.go
@@ -0,0 +1,158 @@
+// Copyright 2021 Fraunhofer AISEC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//           $$\                           $$\ $$\   $$\
+//           $$ |                          $$ |\__|  $$ |
+//  $$$$$$$\ $$ | $$$$$$\  $$\   $$\  $$$$$$$ |$$\ $$$$$$\    $$$$$$\   $$$$$$\
+// $$  _____|$$ |$$  __$$\ $$ |  $$ |$$  __$$ |$$ |\_$$  _|  $$  __$$\ $$  __$$\
+// $$ /      $$ |$$ /  $$ |$$ |  $$ |$$ /  $$ |$$ |  $$ |    $$ /  $$ |$$ | \__|
+// $$ |      $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$\ $$ |  $$ |$$ |
+// \$$$$$$\  $$ |\$$$$$   |\$$$$$   |\$$$$$$  |$$ |  \$$$   |\$$$$$   |$$ |
+//  \_______|\__| \______/  \______/  \_______|\__|   \____/  \______/ \__|
+//
+// This file is part of Clouditor Community Edition.
+
+package assessment
+
+import (
+	"context"
+	"os"
+	"reflect"
+	"testing"
+
+	"clouditor.io/clouditor/api/assessment"
+	"clouditor.io/clouditor/api/evidence"
+	"clouditor.io/clouditor/voc"
+	"github.com/stretchr/testify/assert"
+	"google.golang.org/protobuf/types/known/structpb"
+	"google.golang.org/protobuf/types/known/timestamppb"
+)
+
+func TestMain(m *testing.M) {
+	// make sure, that we are in the clouditor root folder to find the policies
+	err := os.Chdir("../../")
+	if err != nil {
+		panic(err)
+	}
+
+	os.Exit(m.Run())
+}
+
+// TestNewService is a simply test for NewService
+func TestNewService(t *testing.T) {
+	tests := []struct {
+		name string
+		want assessment.AssessmentServer
+	}{
+		{
+			name: "AssessmentServer created with empty results map",
+			want: &Service{
+				results:                       make(map[string]*assessment.Result),
+				UnimplementedAssessmentServer: assessment.UnimplementedAssessmentServer{},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := NewService(); !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("NewService() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+// TestStoreEvidence tests StoreEvidence
+func TestHandleEvidence(t *testing.T) {
+	type args struct {
+		in0      context.Context
+		evidence *evidence.Evidence
+	}
+	tests := []struct {
+		name     string
+		args     args
+		wantResp *evidence.StoreEvidenceResponse
+		wantErr  bool
+	}{
+		{
+			name: "Assess resource without id",
+			args: args{
+				in0: context.TODO(),
+				evidence: &evidence.Evidence{
+					ToolId:    "mock",
+					Timestamp: timestamppb.Now(),
+					Resource:  toStruct(voc.VirtualMachine{}, t),
+				},
+			},
+			wantErr:  true,
+			wantResp: &evidence.StoreEvidenceResponse{Status: true},
+		},
+		{
+			name: "Assess resource without tool id",
+			args: args{
+				in0: context.TODO(),
+				evidence: &evidence.Evidence{
+					Timestamp: timestamppb.Now(),
+					Resource:  toStruct(voc.VirtualMachine{}, t),
+				},
+			},
+			wantErr:  true,
+			wantResp: &evidence.StoreEvidenceResponse{Status: true},
+		},
+		{
+			name: "Assess resource without timestamp",
+			args: args{
+				in0: context.TODO(),
+				evidence: &evidence.Evidence{
+					ToolId:   "mock",
+					Resource: toStruct(voc.VirtualMachine{}, t),
+				},
+			},
+			wantErr:  true,
+			wantResp: &evidence.StoreEvidenceResponse{Status: true},
+		},
+		{
+			name: "Assess resource",
+			args: args{
+				in0: context.TODO(),
+				evidence: &evidence.Evidence{
+					ToolId:    "mock",
+					Timestamp: timestamppb.Now(),
+					Resource:  toStruct(voc.VirtualMachine{Compute: &voc.Compute{CloudResource: &voc.CloudResource{ID: "my-resource-id", Type: []string{"VirtualMachine"}}}}, t),
+				},
+			},
+			wantErr:  false,
+			wantResp: &evidence.StoreEvidenceResponse{Status: true},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			s := NewService()
+
+			_, err := s.AssessEvidence(tt.args.in0, &assessment.AssessEvidenceRequest{Evidence: tt.args.evidence})
+			if (err != nil) != tt.wantErr {
+				t.Errorf("AssessEvidence() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+		})
+	}
+}
+
+func toStruct(r voc.IsCloudResource, t *testing.T) (s *structpb.Value) {
+	s, err := voc.ToStruct(r)
+	if err != nil {
+		assert.NotNil(t, err)
+	}
+
+	return
+}
diff --git a/service/discovery/aws/storage_test.go b/service/discovery/aws/storage_test.go
index 01607a4c..b8a97d13 100644
--- a/service/discovery/aws/storage_test.go
+++ b/service/discovery/aws/storage_test.go
@@ -44,12 +44,12 @@ import (
 
 const (
 	mockBucket1             = "mockbucket1"
-	mockBucket1Endpoint     = "https://mockbucket1.s3.eu-central-1.amazonaws.com"
 	mockBucket1Region       = "eu-central-1"
+	mockBucket1Endpoint     = "https://" + mockBucket1 + ".s3." + mockBucket1Region + ".amazonaws.com"
 	mockBucket1CreationTime = "2012-11-01T22:08:41+00:00"
 	mockBucket2             = "mockbucket2"
-	mockBucket2Endpoint     = "https://mockbucket2.s3.eu-west-1.amazonaws.com"
-	mockBucket2Region       = "eu-west-1"
+	mockBucket2Region       = mockBucket1Region
+	mockBucket2Endpoint     = "https://" + mockBucket2 + ".s3." + mockBucket1Region + ".amazonaws.com"
 	mockBucket2CreationTime = "2013-12-02T22:08:41+00:00"
 	mockBucket2KeyId        = "1234abcd-12ab-34cd-56ed-1234567890ab"
 	mockBucket3             = "mockbucket3"
@@ -134,7 +134,7 @@ func (m mockS3APINew) GetBucketPolicy(_ context.Context,
 				{
 					Action:    "s3:*",
 					Effect:    "Deny",
-					Resource:  []string{"*"},
+					Resource:  "*",
 					Condition: Condition{Bool{AwsSecureTransport: false}},
 				}},
 		}
@@ -158,7 +158,7 @@ func (m mockS3APINew) GetBucketPolicy(_ context.Context,
 				{
 					Action:    "s3:*",
 					Effect:    "Audit",
-					Resource:  []string{"*"},
+					Resource:  "*",
 					Condition: Condition{Bool{AwsSecureTransport: false}},
 				}},
 		}
@@ -289,11 +289,16 @@ func TestAwsS3Discovery_getBuckets(t *testing.T) {
 	d := awsS3Discovery{
 		storageAPI:    mockS3APINew{},
 		isDiscovering: false,
+		awsConfig: &Client{
+			cfg:       aws.Config{Region: mockBucket1Region},
+			accountID: nil,
+		},
 	}
 	buckets, err := d.getBuckets()
 	assert.Nil(t, err)
 
 	log.Print("Testing number of buckets")
+	// We fetch buckets currently only of users region
 	assert.Equal(t, 2, len(buckets))
 
 	log.Print("Testing name of first bucket")
@@ -356,7 +361,7 @@ func TestAwsS3Discovery_getEncryptionAtRest(t *testing.T) {
 	assert.Equal(t, "AES256", managedEncryption.Algorithm)
 
 	// Second case: SSE-KMS encryption
-	encryptionAtRest, err = d.getEncryptionAtRest(bucket{name: mockBucket2, region: "eu-west-1"})
+	encryptionAtRest, err = d.getEncryptionAtRest(bucket{name: mockBucket2, region: mockBucket2Region})
 	customerEncryption, ok = encryptionAtRest.(voc.CustomerKeyEncryption)
 	assert.True(t, ok)
 	assert.Nil(t, err)
@@ -405,9 +410,7 @@ func TestAwsS3Discovery_getTransportEncryption(t *testing.T) {
 	// Case 3: JSON failure
 	encryptionAtTransit, err = d.getTransportEncryption(mockBucket2)
 	assert.NotNil(t, err)
-	assert.True(t, encryptionAtTransit.Enabled)
-	assert.Equal(t, "TLS1.2", encryptionAtTransit.TlsVersion)
-	assert.False(t, encryptionAtTransit.Enforced)
+	assert.Nil(t, encryptionAtTransit)
 
 	// Case 4: Not enforced
 	encryptionAtTransit, err = d.getTransportEncryption(mockBucket3)
@@ -462,7 +465,7 @@ func TestAwsS3Discovery_List(t *testing.T) {
 		isDiscovering: false,
 		awsConfig: &Client{
 			cfg: aws.Config{
-				Region:           "mockRegion",
+				Region:           mockBucket1Region,
 				Credentials:      nil,
 				HTTPClient:       nil,
 				EndpointResolver: nil,
diff --git a/service/discovery/azure/compute_test.go b/service/discovery/azure/compute_test.go
index 63bfd071..ebb93fbf 100644
--- a/service/discovery/azure/compute_test.go
+++ b/service/discovery/azure/compute_test.go
@@ -28,6 +28,7 @@ package azure_test
 import (
 	"net/http"
 	"testing"
+	"time"
 
 	"clouditor.io/clouditor/service/discovery/azure"
 	"clouditor.io/clouditor/voc"
@@ -49,16 +50,24 @@ func (m mockComputeSender) Do(req *http.Request) (res *http.Response, err error)
 					"properties": map[string]interface{}{
 						"diagnosticsProfile": map[string]interface{}{
 							"bootDiagnostics": map[string]interface{}{
-								"enabled": true,
+								"enabled":    true,
+								"storageUri": "https://logstoragevm1.blob.core.windows.net/",
 							},
 						},
 					},
 				},
 				{
-					"id":         "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Compute/virtualMachines/vm2",
-					"name":       "vm2",
-					"location":   "eastus",
-					"properties": map[string]interface{}{},
+					"id":       "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Compute/virtualMachines/vm2",
+					"name":     "vm2",
+					"location": "eastus",
+					"properties": map[string]interface{}{
+						"diagnosticsProfile": map[string]interface{}{
+							"bootDiagnostics": map[string]interface{}{
+								"enabled":    true,
+								"storageUri": "",
+							},
+						},
+					},
 				},
 			},
 		}, 200)
@@ -84,6 +93,12 @@ func (m mockComputeSender) Do(req *http.Request) (res *http.Response, err error)
 						},
 					},
 				},
+				"diagnosticsProfile": map[string]interface{}{
+					"bootDiagnostics": map[string]interface{}{
+						"enabled":    false,
+						"storageUri": "test",
+					},
+				},
 				"networkProfile": map[string]interface{}{
 					"networkInterfaces": &[]map[string]interface{}{
 						{
@@ -118,6 +133,12 @@ func (m mockComputeSender) Do(req *http.Request) (res *http.Response, err error)
 						},
 					},
 				},
+				"diagnosticsProfile": map[string]interface{}{
+					"bootDiagnostics": map[string]interface{}{
+						"enabled":    true,
+						"storageUri": "test",
+					},
+				},
 				"networkProfile": map[string]interface{}{
 					"networkInterfaces": &[]map[string]interface{}{
 						{
@@ -180,6 +201,12 @@ func TestVirtualMachine(t *testing.T) {
 	assert.Equal(t, "123", string(virtualMachine.NetworkInterface[0]))
 	assert.Equal(t, "eastus", virtualMachine.GeoLocation.Region)
 	assert.Equal(t, true, virtualMachine.BootLog.Enabled)
+	assert.Equal(t, voc.ResourceID("https://logstoragevm1.blob.core.windows.net/"), virtualMachine.BootLog.Output[0])
+	assert.Equal(t, time.Duration(0), virtualMachine.BootLog.RetentionPeriod)
+
+	virtualMachine2, ok := list[1].(*voc.VirtualMachine)
+	assert.True(t, ok)
+	assert.Equal(t, voc.ResourceID(""), virtualMachine2.BootLog.Output[0])
 }
 
 func TestFunction(t *testing.T) {
diff --git a/service/discovery/azure/iac_template_test.go b/service/discovery/azure/iac_template_test.go
index e46eccb4..f1b9d455 100644
--- a/service/discovery/azure/iac_template_test.go
+++ b/service/discovery/azure/iac_template_test.go
@@ -367,6 +367,7 @@ func TestVmProperties(t *testing.T) {
 	assert.Equal(t, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Compute/disks/blockStorage3", (string)(resourceVM.BlockStorage[0]))
 	assert.Equal(t, "eastus", resourceVM.GeoLocation.Region)
 	assert.True(t, resourceVM.BootLog.Enabled)
+	assert.Equal(t, voc.ResourceID("https://storage1.blob.core.windows.net/"), resourceVM.BootLog.Output[0])
 	assert.False(t, resourceVM.OSLog.Enabled)
 }
 
diff --git a/service/discovery/azure/storage_test.go b/service/discovery/azure/storage_test.go
index 7af3abaf..f5235f4c 100644
--- a/service/discovery/azure/storage_test.go
+++ b/service/discovery/azure/storage_test.go
@@ -26,11 +26,12 @@
 package azure_test
 
 import (
-	"clouditor.io/clouditor/voc"
-	"github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2021-04-01/storage"
 	"net/http"
 	"testing"
 
+	"clouditor.io/clouditor/voc"
+	"github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2021-04-01/storage"
+
 	"clouditor.io/clouditor/service/discovery/azure"
 	"github.com/stretchr/testify/assert"
 )
@@ -262,7 +263,7 @@ func TestObjectStorage(t *testing.T) {
 	assert.Equal(t, true, objectStorage.HttpEndpoint.TransportEncryption.Enabled)
 	assert.Equal(t, true, objectStorage.HttpEndpoint.TransportEncryption.Enforced)
 	assert.Equal(t, "TLS1_2", objectStorage.HttpEndpoint.TransportEncryption.TlsVersion)
-	assert.Equal(t, "", objectStorage.HttpEndpoint.TransportEncryption.Algorithm)
+	assert.Equal(t, "TLS", objectStorage.HttpEndpoint.TransportEncryption.Algorithm)
 
 	// Check ManagedKeyEncryption
 	atRestEncryption := *objectStorage.GetAtRestEncryption()
@@ -322,7 +323,7 @@ func TestFileStorage(t *testing.T) {
 	assert.Equal(t, true, fileStorage.HttpEndpoint.TransportEncryption.Enabled)
 	assert.Equal(t, true, fileStorage.HttpEndpoint.TransportEncryption.Enforced)
 	assert.Equal(t, "TLS1_2", fileStorage.HttpEndpoint.TransportEncryption.TlsVersion)
-	assert.Equal(t, "", fileStorage.HttpEndpoint.TransportEncryption.Algorithm)
+	assert.Equal(t, "AES256", fileStorage.HttpEndpoint.TransportEncryption.Algorithm)
 }
 
 func TestListBlockStorage(t *testing.T) {
diff --git a/service/discovery/discovery_test.go b/service/discovery/discovery_test.go
index 9daf1f38..d15b5e44 100644
--- a/service/discovery/discovery_test.go
+++ b/service/discovery/discovery_test.go
@@ -101,16 +101,14 @@ func TestStartDiscovery(t *testing.T) {
 
 			// APIs for assessment and evidence store both send the same evidence. Thus, testing one is enough.
 			if tt.checkEvidence {
-				evidence := discoveryService.AssessmentStream.(*mockAssessmentStream).sentEvidence
+				e := discoveryService.AssessmentStream.(*mockAssessmentStream).sentEvidence
 				// Check if UUID has been created
-				assert.NotEmpty(t, evidence.Id)
+				assert.NotEmpty(t, e.Id)
 				// Check if cloud resources / properties are there
-				assert.NotEmpty(t, evidence.Resource)
-				// Currently, hard coded in discovery. This assertion will probably fail in future
-				assert.Equal(t, []int32{1, 2}, evidence.ApplicableMetrics)
+				assert.NotEmpty(t, e.Resource)
 				// Check if ID of mockDiscovery's resource is mapped to resource id of the evidence
 				list, _ := tt.fields.discoverer.List()
-				assert.Equal(t, string(list[0].GetID()), evidence.ResourceId)
+				assert.Equal(t, string(list[0].GetID()), e.Resource.GetStructValue().AsMap()["id"].(string))
 			}
 		})
 	}
diff --git a/service/evidence/evidence_store_test.go b/service/evidence/evidence_store_test.go
index 6f5f2160..01ae0436 100644
--- a/service/evidence/evidence_store_test.go
+++ b/service/evidence/evidence_store_test.go
@@ -7,9 +7,11 @@ import (
 	"testing"
 
 	"google.golang.org/protobuf/types/known/emptypb"
+	"google.golang.org/protobuf/types/known/structpb"
 	"google.golang.org/protobuf/types/known/timestamppb"
 
 	"clouditor.io/clouditor/api/evidence"
+	"clouditor.io/clouditor/voc"
 	"github.com/stretchr/testify/assert"
 	"google.golang.org/grpc/metadata"
 )
@@ -54,13 +56,16 @@ func TestStoreEvidence(t *testing.T) {
 			args: args{
 				in0: context.TODO(),
 				evidence: &evidence.Evidence{
-					Id:                "MockEvidenceId",
-					ServiceId:         "MockServiceId",
-					ResourceId:        "MockResourceId",
-					Timestamp:         timestamppb.Now(),
-					ApplicableMetrics: []int32{1, 2},
-					Raw:               "",
-					Resource:          nil,
+					Id:        "MockEvidenceId",
+					ServiceId: "MockServiceId",
+					ToolId:    "MockTool",
+					Timestamp: timestamppb.Now(),
+					Raw:       "",
+					Resource: toStruct(voc.VirtualMachine{
+						Compute: &voc.Compute{CloudResource: &voc.CloudResource{
+							ID: "mock-id",
+						}},
+					}, t),
 				},
 			},
 			wantErr:  false,
@@ -114,22 +119,18 @@ func TestStoreEvidences(t *testing.T) {
 func TestListEvidences(t *testing.T) {
 	s := NewService()
 	s.evidences["MockEvidenceId-1"] = &evidence.Evidence{
-		Id:                "MockEvidenceId-1",
-		ServiceId:         "MockServiceId-1",
-		ResourceId:        "MockResourceId-1",
-		Timestamp:         timestamppb.Now(),
-		ApplicableMetrics: []int32{1, 2},
-		Raw:               "",
-		Resource:          nil,
+		Id:        "MockEvidenceId-1",
+		ServiceId: "MockServiceId-1",
+		Timestamp: timestamppb.Now(),
+		Raw:       "",
+		Resource:  nil,
 	}
 	s.evidences["MockEvidenceId-2"] = &evidence.Evidence{
-		Id:                "MockEvidenceId-2",
-		ServiceId:         "MockServiceId-2",
-		ResourceId:        "MockResourceId-2",
-		Timestamp:         timestamppb.Now(),
-		ApplicableMetrics: []int32{1, 2},
-		Raw:               "",
-		Resource:          nil,
+		Id:        "MockEvidenceId-2",
+		ServiceId: "MockServiceId-2",
+		Timestamp: timestamppb.Now(),
+		Raw:       "",
+		Resource:  nil,
 	}
 
 	resp, err := s.ListEvidences(context.TODO(), &evidence.ListEvidencesRequest{})
@@ -149,24 +150,20 @@ func (m *mockStreamer) Recv() (*evidence.Evidence, error) {
 	if m.counter == 0 {
 		m.counter++
 		return &evidence.Evidence{
-			Id:                "MockEvidenceId-1",
-			ServiceId:         "MockServiceId-1",
-			ResourceId:        "MockResourceId-1",
-			Timestamp:         timestamppb.Now(),
-			ApplicableMetrics: []int32{1, 2},
-			Raw:               "",
-			Resource:          nil,
+			Id:        "MockEvidenceId-1",
+			ServiceId: "MockServiceId-1",
+			Timestamp: timestamppb.Now(),
+			Raw:       "",
+			Resource:  nil,
 		}, nil
 	} else if m.counter == 1 {
 		m.counter++
 		return &evidence.Evidence{
-			Id:                "MockEvidenceId-2",
-			ServiceId:         "MockServiceId-2",
-			ResourceId:        "MockResourceId-2",
-			Timestamp:         timestamppb.Now(),
-			ApplicableMetrics: []int32{1, 2},
-			Raw:               "",
-			Resource:          nil,
+			Id:        "MockEvidenceId-2",
+			ServiceId: "MockServiceId-2",
+			Timestamp: timestamppb.Now(),
+			Raw:       "",
+			Resource:  nil,
 		}, nil
 	} else {
 		return nil, io.EOF
@@ -196,3 +193,12 @@ func (mockStreamer) SendMsg(_ interface{}) error {
 func (mockStreamer) RecvMsg(_ interface{}) error {
 	panic("implement me")
 }
+
+func toStruct(r voc.IsCloudResource, t *testing.T) (s *structpb.Value) {
+	s, err := voc.ToStruct(r)
+	if err != nil {
+		assert.NotNil(t, err)
+	}
+
+	return
+}
diff --git a/service/orchestrator/orchestrator_test.go b/service/orchestrator/orchestrator_test.go
index d183994e..2ccf4ea4 100644
--- a/service/orchestrator/orchestrator_test.go
+++ b/service/orchestrator/orchestrator_test.go
@@ -26,11 +26,12 @@
 package orchestrator_test
 
 import (
-	"clouditor.io/clouditor/api/assessment"
 	"context"
 	"io/fs"
 	"testing"
 
+	"clouditor.io/clouditor/api/assessment"
+
 	"clouditor.io/clouditor/api/orchestrator"
 	service_orchestrator "clouditor.io/clouditor/service/orchestrator"
 	"github.com/stretchr/testify/assert"
@@ -58,7 +59,7 @@ func TestGetMetric(t *testing.T) {
 	)
 
 	request = &orchestrator.GetMetricsRequest{
-		MetricId: 1,
+		MetricId: "TransportEncryptionEnabled",
 	}
 
 	metric, err = service.GetMetric(context.TODO(), request)