Repository: prometheus/prometheus (language: go)

Issue:
ec2_sd_configs: make 'region' available as a metadata label
## Scenario

Suppose you have a single scrape job that scrapes many EC2 accounts or regions, or combinations of account+region:

```
  - job_name: foo
    ec2_sd_configs:
      - access_key: AKIA.....
        secret_key: XXXX.....
        region: eu-west-1
      - access_key: AKIA.....
        secret_key: XXXX.....
        region: eu-west-2
      ...etc
    relabel_configs:
      - source_labels: [__meta_ec2_instance_id]
        target_label: instance
      - source_labels: [__meta_ec2_owner_id]
        target_label: owner_id
      ...etc
```

Within the target relabelling config, it's easy to access the account ID (it's `__meta_ec2_owner_id`).  But there doesn't seem to be an item which identifies the region.

It would be very tedious to have to make duplicate scrape jobs for every region, which differ only in the one label they apply.

## Proposal

Option 1: set `__meta_ec2_region` in the target label set, to the region which was requested in the DescribeInstances API call (if any). That's a very simple change.

Option 2: I would also be quite happy if I could do something like this:

```
    ec2_sd_configs:
      - access_key: AKIA.....
        secret_key: XXXX.....
        region: eu-west-1
        labels:
          region: eu-west-1
      - access_key: AKIA.....
        secret_key: XXXX.....
        region: eu-west-2
        labels:
          region: eu-west-2
```

However, currently only static_configs and file_sd_configs permit applying additional labels to a group of targets in this way.  This is a more profound change across SD mechanisms, and IMO wouldn't be worth doing unless there were additional use cases that this would fix.

## Workaround

I discovered that the `private_dns_name` metadata usually is of the form `<foo>.<region>.compute.internal`, except for the us-east-1 region where it's `<foo>.ec2.internal`.

This is [documented](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-naming.html) to be the case for "the guest OS hostname", in either of the available formats.

Hence this appears to work:

```
      - source_labels: [__meta_ec2_private_dns_name]
        regex: '.*[.]ec2[.]internal'
        replacement: us-east-1
        target_label: region
      - source_labels: [__meta_ec2_private_dns_name]
        regex: '.*[.]([^.]+)[.]compute[.]internal'
        target_label: region
```

However it feels fragile to depend on this.


Discussion / hints:
Your option 1 sounds like the best and most straightforward way to me too. I don't have any EC2 stuff, but do you want to try adding it to https://github.com/prometheus/prometheus/blob/main/discovery/aws/ec2.go? You already have access to the region via `d.cfg.Region` in `refresh()`, so you could just put that into a new meta label somewhere here: https://github.com/prometheus/prometheus/blob/main/discovery/aws/ec2.go#L243-L245

Fix patch:
diff --git a/discovery/aws/ec2.go b/discovery/aws/ec2.go
index 9ae964c8346..7519f58da15 100644
--- a/discovery/aws/ec2.go
+++ b/discovery/aws/ec2.go
@@ -57,6 +57,7 @@ const (
 	ec2LabelPrivateIP         = ec2Label + "private_ip"
 	ec2LabelPublicDNS         = ec2Label + "public_dns_name"
 	ec2LabelPublicIP          = ec2Label + "public_ip"
+	ec2LabelRegion            = ec2Label + "region"
 	ec2LabelSubnetID          = ec2Label + "subnet_id"
 	ec2LabelTag               = ec2Label + "tag_"
 	ec2LabelVPCID             = ec2Label + "vpc_id"
@@ -242,6 +243,7 @@ func (d *EC2Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error
 
 				labels := model.LabelSet{
 					ec2LabelInstanceID: model.LabelValue(*inst.InstanceId),
+					ec2LabelRegion:     model.LabelValue(d.cfg.Region),
 				}
 
 				if r.OwnerId != nil {
diff --git a/discovery/aws/lightsail.go b/discovery/aws/lightsail.go
index 7f89312b336..016d78a67f7 100644
--- a/discovery/aws/lightsail.go
+++ b/discovery/aws/lightsail.go
@@ -49,6 +49,7 @@ const (
 	lightsailLabelIPv6Addresses       = lightsailLabel + "ipv6_addresses"
 	lightsailLabelPrivateIP           = lightsailLabel + "private_ip"
 	lightsailLabelPublicIP            = lightsailLabel + "public_ip"
+	lightsailLabelRegion              = lightsailLabel + "region"
 	lightsailLabelTag                 = lightsailLabel + "tag_"
 	lightsailLabelSeparator           = ","
 )
@@ -199,6 +200,7 @@ func (d *LightsailDiscovery) refresh(ctx context.Context) ([]*targetgroup.Group,
 			lightsailLabelInstanceState:       model.LabelValue(*inst.State.Name),
 			lightsailLabelInstanceSupportCode: model.LabelValue(*inst.SupportCode),
 			lightsailLabelPrivateIP:           model.LabelValue(*inst.PrivateIpAddress),
+			lightsailLabelRegion:              model.LabelValue(d.cfg.Region),
 		}
 
 		addr := net.JoinHostPort(*inst.PrivateIpAddress, fmt.Sprintf("%d", d.cfg.Port))
diff --git a/docs/configuration/configuration.md b/docs/configuration/configuration.md
index d89bcc8ee41..d0add7be708 100644
--- a/docs/configuration/configuration.md
+++ b/docs/configuration/configuration.md
@@ -1016,6 +1016,7 @@ The following meta labels are available on targets during [relabeling](#relabel_
 * `__meta_ec2_private_ip`: the private IP address of the instance, if present
 * `__meta_ec2_public_dns_name`: the public DNS name of the instance, if available
 * `__meta_ec2_public_ip`: the public IP address of the instance, if available
+* `__meta_ec2_region`: the region of the instance
 * `__meta_ec2_subnet_id`: comma separated list of subnets IDs in which the instance is running, if available
 * `__meta_ec2_tag_<tagkey>`: each tag value of the instance
 * `__meta_ec2_vpc_id`: the ID of the VPC in which the instance is running, if available
@@ -1968,6 +1969,7 @@ The following meta labels are available on targets during [relabeling](#relabel_
 * `__meta_lightsail_ipv6_addresses`: comma separated list of IPv6 addresses assigned to the instance's network interfaces, if present
 * `__meta_lightsail_private_ip`: the private IP address of the instance
 * `__meta_lightsail_public_ip`: the public IP address of the instance, if available
+* `__meta_lightsail_region`: the region of the instance
 * `__meta_lightsail_tag_<tagkey>`: each tag value of the instance
 
 See below for the configuration options for Lightsail discovery:


Test patch: