From ade9c36057a4a23029adab441be4a6567cc2cfad Mon Sep 17 00:00:00 2001 From: Victoria Jeffrey Date: Wed, 20 Sep 2023 22:31:00 -0600 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20add=20state=20transition=20time=20t?= =?UTF-8?q?o=20aws=20ec2=20instance?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- providers/aws/resources/aws.lr | 2 ++ providers/aws/resources/aws.lr.go | 12 ++++++++++++ providers/aws/resources/aws.lr.manifest.yaml | 1 + providers/aws/resources/aws_ec2.go | 14 +++++++++++++- 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/providers/aws/resources/aws.lr b/providers/aws/resources/aws.lr index 7836fe78c9..55eeb46bda 100644 --- a/providers/aws/resources/aws.lr +++ b/providers/aws/resources/aws.lr @@ -1659,6 +1659,8 @@ private aws.ec2.instance @defaults("arn state") { privateDnsName string // Keypair associated with the instance keypair() aws.ec2.keypair + // Time when the last state transition occurred + stateTransitionTime time } // Amazon EC2 Key Pair diff --git a/providers/aws/resources/aws.lr.go b/providers/aws/resources/aws.lr.go index c115b9ef8f..383886840e 100644 --- a/providers/aws/resources/aws.lr.go +++ b/providers/aws/resources/aws.lr.go @@ -2373,6 +2373,9 @@ var getDataFields = map[string]func(r plugin.Resource) *plugin.DataRes{ "aws.ec2.instance.keypair": func(r plugin.Resource) *plugin.DataRes { return (r.(*mqlAwsEc2Instance).GetKeypair()).ToDataRes(types.Resource("aws.ec2.keypair")) }, + "aws.ec2.instance.stateTransitionTime": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlAwsEc2Instance).GetStateTransitionTime()).ToDataRes(types.Time) + }, "aws.ec2.keypair.arn": func(r plugin.Resource) *plugin.DataRes { return (r.(*mqlAwsEc2Keypair).GetArn()).ToDataRes(types.String) }, @@ -5411,6 +5414,10 @@ var setDataFields = map[string]func(r plugin.Resource, v *llx.RawData) bool { r.(*mqlAwsEc2Instance).Keypair, ok = plugin.RawToTValue[*mqlAwsEc2Keypair](v.Value, v.Error) return }, + "aws.ec2.instance.stateTransitionTime": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlAwsEc2Instance).StateTransitionTime, ok = plugin.RawToTValue[*time.Time](v.Value, v.Error) + return + }, "aws.ec2.keypair.__id": func(r plugin.Resource, v *llx.RawData) (ok bool) { r.(*mqlAwsEc2Keypair).__id, ok = v.Value.(string) return @@ -14587,6 +14594,7 @@ type mqlAwsEc2Instance struct { PrivateIp plugin.TValue[string] PrivateDnsName plugin.TValue[string] Keypair plugin.TValue[*mqlAwsEc2Keypair] + StateTransitionTime plugin.TValue[*time.Time] } // createAwsEc2Instance creates a new instance of this resource @@ -14756,6 +14764,10 @@ func (c *mqlAwsEc2Instance) GetKeypair() *plugin.TValue[*mqlAwsEc2Keypair] { }) } +func (c *mqlAwsEc2Instance) GetStateTransitionTime() *plugin.TValue[*time.Time] { + return &c.StateTransitionTime +} + // mqlAwsEc2Keypair for the aws.ec2.keypair resource type mqlAwsEc2Keypair struct { MqlRuntime *plugin.Runtime diff --git a/providers/aws/resources/aws.lr.manifest.yaml b/providers/aws/resources/aws.lr.manifest.yaml index ff837b0f4e..40008a60eb 100755 --- a/providers/aws/resources/aws.lr.manifest.yaml +++ b/providers/aws/resources/aws.lr.manifest.yaml @@ -502,6 +502,7 @@ resources: state: {} stateReason: {} stateTransitionReason: {} + stateTransitionTime: {} tags: {} vpc: {} is_private: true diff --git a/providers/aws/resources/aws_ec2.go b/providers/aws/resources/aws_ec2.go index dc3cfa21d4..48fc0225ff 100644 --- a/providers/aws/resources/aws_ec2.go +++ b/providers/aws/resources/aws_ec2.go @@ -6,8 +6,10 @@ package resources import ( "context" "fmt" + "regexp" "strconv" "strings" + "time" "github.com/aws/aws-sdk-go-v2/service/ec2" ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" @@ -692,7 +694,16 @@ func (a *mqlAwsEc2) gatherInstanceInfo(instances []ec2types.Reservation, imdsvVe if err != nil { return nil, err } - + var stateTransitionTime time.Time + reg := regexp.MustCompile(`.*\((\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}) GMT\)`) + timeString := reg.FindStringSubmatch(convert.ToString(instance.StateTransitionReason)) + if len(timeString) == 2 { + stateTransitionTime, err = time.Parse(time.DateTime, timeString[1]) + if err != nil { + log.Error().Err(err).Msg("cannot parse state transition time for ec2 instance") + stateTransitionTime = llx.NeverPastTime + } + } args := map[string]*llx.RawData{ "platformDetails": llx.StringData(convert.ToString(instance.PlatformDetails)), "arn": llx.StringData(fmt.Sprintf(ec2InstanceArnPattern, regionVal, conn.AccountId(), convert.ToString(instance.InstanceId))), @@ -713,6 +724,7 @@ func (a *mqlAwsEc2) gatherInstanceInfo(instances []ec2types.Reservation, imdsvVe "launchTime": llx.TimeData(toTime(instance.LaunchTime)), "privateIp": llx.StringData(convert.ToString(instance.PrivateIpAddress)), "privateDnsName": llx.StringData(convert.ToString(instance.PrivateDnsName)), + "stateTransitionTime": llx.TimeData(stateTransitionTime), } if instance.ImageId != nil {