2

Trying to get a substring out of a larger string from an Azure Subnet. The vnet resource id is identical to the subnet resource id minus the last 2 segments. User inputs the resource id of the subnet, and I want to get the resource id of the vnet out of it. The vnets could have different naming conventions, so the length of the "substring" won't always be the same, but the vnet id will always be the subnet id minus the last 2 segments.

For example, is there an easy way to get string2 from string 1, where the length of string 2 might vary. Let's say string1 is:

abc/def/ghi/jkl/mno

and I want to get the first 3 segments of this string so that string2 will be:

abc/def/ghi

Is there an easy operation that can be performed that will cut off the last 2 segments despite the length of, in this case, the first three? To clarify with different lengths, if the user then puts in a different string1:

abcdef/ghi/jklmn/opqr/stuvwx

string2 should be:

abcdef/ghi/jklmn

I have tried using .split but it only gives me the text after the delimiter and .substring I believe needs a start and end index, which again might vary.

1
  • maybe 'abcdef/ghi/jklmn/opqr/stuvwx' -replace '(?<=(?:.+/){2}.+)/.+' Commented Jun 4 at 20:47

3 Answers 3

2

tl;dr

An example subnet id in Azure might be something like:

/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx/resourceGroups/rg-myrg/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/my-subnet

To get the virtual network (vnet) id you want to remove the last 2 segments:

$subnet_id = "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx/resourceGroups/rg-myrg/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/my-subnet"

$parts = $subnet_id.Split("/")
$vnet_id = $parts[0..($parts.Length-3)] -join "/"

$vnet_id
# /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx/resourceGroups/rg-myrg/providers/Microsoft.Network/virtualNetworks/my-vnet

Details

The first step is to split the subnet id into an array of parts:

PS> $subnet_id.Split("/")

subscriptions
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx
resourceGroups
rg-myrg
providers
Microsoft.Network
virtualNetworks
my-vnet
subnets
my-subnet

Note the first item is an empty string because the subnet id starts with a separator.

It then uses the Range operator to generate an array of integers from zero to "the number of parts in the array minus 3":

PS> 0..($parts.Length-3)

# 0
# 1
# 2
# 3
# 4
# 5
# 6
# 7
# 8

And uses array slicing to extract the items at each of those indices

PS> $parts[0..($parts.Length-3)]

subscriptions
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx
resourceGroups
rg-myrg
providers
Microsoft.Network
virtualNetworks
my-vnet 

(Note the first item in the result is still an empty string)

It then finally joins those values into a single string with the -join operator:

PS> $parts[0..($parts.Length-3)] -join "/"

/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx/resourceGroups/rg-myrg/providers/Microsoft.Network/virtualNetworks/my-vnet

which gives your vnet id.

Sign up to request clarification or add additional context in comments.

4 Comments

Nicely done; in PowerShell (Core) 7, you can simplify to ('/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx/resourceGroups/rg-myrg/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/my-subnet' -split '/', -3)[0]
@mklement0 - what is this witchcraft? :-). (/me goes to read fine print in documentation for -split)
Thank you! This is what I was looking for. I figured this was roughly the path I needed to take but wasn't sure how to accomplish it.
@mclayton :) Since this PS 7-only feature of the -split operator doesn't get enough love and is only tersely documented, I've posted an answer explaining it in detail.
2

With .Split() you can pick the elements 0 to 2 then re-join them:

'abc/def/ghi/jkl/mno'.Split('/')[0..2] -join '/'

With -replace you can use a regex like https://regex101.com/r/vxkvGd/1:

'abc/def/ghi/jkl/mno' -replace '(?<=(?:.+/){2}.+)/.+'

2 Comments

Nicely done; just to show an all-PowerShell-operators alternative to the .Split() solution: ('abc/def/ghi/jkl/mno/x/foo/aha' -split '/')[0..2] -join '/'
+1, but I think the OP's question might be a little bit misleading - they really want to keep everything except the last 2 parts. In their example they've shown 5 parts so that means the first 3, but an azure virtual subnet id has 10 or 11 parts (depending on if you count the empty one at the start) so they really want the first 8 or 9 (or all but the last 2).
1

To complement mclayton's helpful answer with a more concise PowerShell (Core) 7 solution that relies on an enhancement to the -split operator that is not available in Windows PowerShell, the legacy, ships-with-Windows, Windows-only edition of PowerShell whose latest and last version is 5.1:

# PowerShell 7 only:
# Remove the *last 2* path segments, irrespective of the total number of segments.
# -> 'one/two/three'
('one/two/three/four/five' -split '/', -3)[0]

Read on for an explanation.


Context and explanation:

  • In both PowerShell editions, -split optionally allows you to specify the maximum number of tokens to return, beginning from the start of the input string; e.g.:

    # -> @('one', 'two', 'three/four/five') 
    'one/two/three/four/five' -split '/', 3
    
    • That is, 3 requests that at most 3 /-separated tokens be returned, which means that splitting stops after the 2nd separator, with the first 2 tokens each forming the first and second element of the return array, and the remainder of the input string - whether or not it contains further separator instances - forming the last element.

    • You can use this technique to remove the first N tokens from a separated string, namely by passing N+1 as the max. token count and accessing the last element of the return array; applied to the example above:

      # To remove the first N (2) tokens, pass N+1 (3) and 
      # extract the last resulting array element ([-1])
      # -> 'three/four/five'
      ('one/two/three/four/five' -split '/', 3)[-1]
      
  • In PowerShell (Core) 7 only, you may alternatively request that splitting start from the end of the input string, by specifying a negative token count; e.g.:

    # -> @('one/two/three', 'four', 'five') 
    'one/two/three/four/five' -split '/', -3
    
    • That is, -3 requests that at most 3 /-separated tokens be returned starting from the end of the input string, which means that splitting stops after the 2nd separator, with the last 2 tokens each forming the last and next-to-last element of the return array, and the remainder of the input string - whether or not it contains further separator instances - forming the first element.

      • Note that, as in the positive-number, from-the-start case, the array is populated in input order, i.e. the return-array elements reflect the order in which the tokens occur in the input string.
    • You can use this technique to remove the last N tokens from a separated string, namely by passing -(N+1) as the max. token count and accessing the first element of the return array; applied to the example above:

      # To remove the last N (2) tokens, pass -(N+1) (-3) and 
      # extract the last resulting array element ([-1])
      # -> 'one/two/three'
      ('one/two/three/four/five' -split '/', -3)[0]
      

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.