Regex for everything but partial substring - regex

I'm usually pretty good with regex but this one has me stumped. I've read the other SO posts about negative looks but nothing seemed to work. I'm looking for a pure regex solution (not piped through anything like grep) because this has to be a single operation (in Atom IDE search).
Here's the issue:
Looking for all instances of the word "core" that are NOT preceded by "vpc_" and specifically capturing "core", not lines that contain core, because this is for Find/Replace, so I don't want to "select" the whole line, just the word core.
EDIT:
resource "aws_internet_gateway" "igw_core" {
vpc_id = "${aws_vpc.vpc_core.id}"
tags {
Environment = "${var.environment}"
Name = "${var.environment}_igw_core"
Version = "${var.version}"
}
}
I want to replace "core" with "foo" except "vpc_core" which should remain. The result should be:
resource "aws_internet_gateway" "igw_foo" {
vpc_id = "${aws_vpc.vpc_core.id}"
tags {
Environment = "${var.environment}"
Name = "${var.environment}_igw_foo"
Version = "${var.version}"
}
}

Related

Terraform - Check If Variable Ends With String & Remove

We've set up our TF GKE code so that the user can specify either the region or zone for the cluster.
However, we need to then check this variable and remove the zone suffix (if it exists) for the deployment of static IP addresses.
We have the following variable:
variable "k8s_cluster_location" {
type = string
default = "europe-west2"
validation {
condition = contains(["europe-west2", "europe-west2-a", "europe-west2-b", "europe-west2-c", "us-east4", "us-east4-a", "us-east4-b", "us-east4-c", "europe-west1", "europe-west1-a", "europe-west1-b", "europe-west1-c" ], var.k8s_cluster_location)
error_message = "Given GCP location not (yet) supported. Contact X if you think it should..."
}
description = "Location of the Kubernetes cluster."
}
If, for example, the variable is "europe-west2-a", we need to remove "-a" to acquire the parent region.
Would we need to incorporate a Regex check? Or could we use something like StartsWith()/EndsWith()?
I would definitely recommend the regular expression solution here as you suggest:
variable "k8s_cluster_location" {
type = string
default = "europe-west2"
validation {
condition = can(regex("(?:europe-west[12])|(?:us-east4)", var.k8s_cluster_location))
error_message = "Given GCP location not (yet) supported. Contact X if you think it should..."
}
description = "Location of the Kubernetes cluster."
}
Note that if you are using Terraform 1.3.x, then you can also use the var.k8s_cluster_location value in the error_message instead of "Given GCP location".
For your other suggestion of startswith(), you would need to do something like anytrue(startswith(var.k8s_cluster_location, "europe-west1"), startswith(var.k8s_cluster_location, "europe-west2"), startswith(var.k8s_cluster_location, "us-east4")), but that feels slightly messier to me.

Terraform internal variable in resource definition

I have this question that I am not sure how to formulate. So let use an example. I have the following resource definition for was subnets based in an input parameter variable:
resource "aws_subnet" "monitoring_subnetwork" {
count = length(var.monitoring_subnets)
vpc_id = module.vpc.vpc_id
cidr_block = var.monitoring_subnets[count.index]
availability_zone= "${data.aws_availability_zones.available.names[count.index % length(data.aws_availability_zones.available.names)]}"
tags = {
Name = "Monitoring private-1${replace(
data.aws_availability_zones.available.names[count.index % length(data.aws_availability_zones.available.names)],
data.aws_availability_zones.available.id, "")}"
}
}
I want to simplify this code to make it more readable and maintainable.
I use a count.index to get an availability zone using round-robin, based on index % len_of_array, and the result of this mod is calculated twice (in other cases even three times).
I wonder if I could define an internal variable inside the resource, something like this:
zone_index = count.index % length(data.aws_availability_zones.available.names)
And reuse this index in the parts of the code where this operation is repeated.
Any thoughts? Also, any other recommendation to simplify this configuration would be appreciated :)
Sadly you can't do this. There are no custom functions in terraform. But there is already a github issue for that, so maybe in future there will be added:
Extending terraform with custom functions
So you have to keep repeating that computation.

Multi match in re2 regex for terraform

I'm trying to apply regex in something like this, in terraform.
variable "version" {
type = "string"
default = https://s3-us-east-1.amazonaws.com/bucket/folder/item-v1.2/item-item2-v1.2.gz
description = "version"
}
name = "${replace(var.version, "//", "")}"
I just need to replace everything and output only "v1.2" for the name, specifically from the item-v1.2 file path. I was thinking of doing something along the lines of
name = "${replace(var.version, "//", "")}"
wherein I replace everything but v1.2.
Any idea how to do it? Thank you in advance!
There are a couple of issues with your variable definition (e.g. wrong use of quotes, variable name "version" is reserved due to its special meaning inside module blocks, and so on) so I operate on the following:
variable "my_version" {
type = string
default = "https://s3-us-east-1.amazonaws.com/bucket/folder/item-v1.2/item-item2-v1.2.gz"
description = "version"
}
Then you can extract the version (or replace everything else except the version as requested) from the sub-folder path like this (this has been run and tested with terraform console):
$ terraform console
> var.my_version
"https://s3-us-east-1.amazonaws.com/bucket/folder/item-v1.2/item-item2-v1.2.gz"
> regex(".*-(.*)/.*.gz", var.my_version)[0]
"v1.2"
> replace(var.my_version, "/.*-(.*)/.*.gz/", "<before> $1 <after>")
"<before> v1.2 <after>"

Select where tag end in a or b in Terraform data lookup

I have 3 subnets. They are named:
test-subnet-az-a test-subnet-az-b test-subnet-az-c
I have a datasource like so:
data "aws_subnet_ids" "test" {
vpc_id = "${module.vpc.id}"
tags = {
Name = "test-subnet-az-*"
}
}
This will return a list including all 3 subnets.
How do I return just the first 2, or those ending in a or b?
Terraform data sources are generally constrained by the capabilities of whatever underlying system they are querying, so the filtering supported by aws_subnet_ids is the same filtering supported by the underlying API, and so reviewing that API (EC2's DescribeSubnets) may show some variants you could try.
With that said, if you can use the data source in a way that is close enough to reduce the resultset down to a manageable size (which you seem to have achieved here) then you can filter the rest of the way using a for expression within the Terraform language itself:
data "aws_subnet_ids" "too_many" {
vpc_id = "${module.vpc.id}"
tags = {
Name = "test-subnet-az-*"
}
}
locals {
want_suffixes = toset(["a", "b"])
subnet_ids = toset([
for s in data.aws_subnet_ids.too_many.ids : s
if contains(local.want_suffixes, substr(s, length(s)-1, 1))
])
}
You can place any condition expression you like after if in that for expression to apply additional filters to the result, and then use local.subnet_ids elsewhere in the configuration to access that reduced set.
I used toset here to preserve the fact that aws_subnet_ids returns a set of strings value rather than a list of strings, but that's not particularly important unless you intend to use the result with a Terraform feature that requires a set, such as the for_each argument within resource and data blocks (which is not yet released as I write this, but should be released soon.)

FW/1 pattern matching N digits

I am trying to match routes where IDs have exactly 6 numbers
This does not work:
variables.framework.routes = [
{ "main/{id:[0-9]{6}}" = "main/home/eid/:id"},
{ "main/home" = "main/home"},
{ "*" = "main/404"}
];
This does:
variables.framework.routes = [
{ "main/{id:[0-9]+}" = "main/home/eid/:id"},
{ "main/home" = "main/home"},
{ "*" = "main/404"}
];
The second one of course matches on any number of digits. I wonder if I have to escape the {
It looks like FW/1 only allows a limited regular expression syntax for the routes declaration. So I don't think your first example will work. From what I could find the limited regular expression syntax in routes was added to FW/1 version 3.5. I found some discussion on the topic and this specific comment describing the requested behavior - https://github.com/framework-one/fw1/issues/325#issuecomment-118572702
{placeholder:regex}, so we could have product/{id:[0-9]+}-:name.html that targets product.detail?id={id:[0-9]+}&name=:name.
You need to repeat the placeholder with the regex in the target route too (could be changed).
You can't put } in your placeholder specific regex.
Let me know if a PR is welcome for this add-on.
Notice that second bullet point which mentions that the } (bracket) is not allowed in the placeholder regex.
Here is a link to the code referenced by that pull-request which was included in 3.5 - https://github.com/framework-one/fw1/commit/9543b78552dbd27a526083ac72a3846bd86eeb90
And here is a link to the updated documentation for version 3.5 where some information was added about this feature - http://framework-one.github.io/documentation/developing-applications.html#url-routes
Snippet of that doc here:
Placeholder variables in the route are identified either by a leading colon or by braces (specifying a variable name and a regex to restrict matches) and can appear in the URL as well, for example { "/product/:id" = "/product/view/id/:id" } specifies a match for /product/something which will be treated as if the URL was /product/view/id/something - section: product, item: view, query string id=something. Similarly, { "/product/{id:[0-9]+}" = "/product/view/id/:id" } specifies a match for /product/42 which will be treated as if the URL was /product/view/id/42, and only numeric values will match the placeholder.