Link to home
Start Free TrialLog in
Avatar of sirbounty
sirbountyFlag for United States of America

asked on

Replacing a portion of a string with function results

I am using an ldap query that tests for various levels of specific department codes.
The problem is, during re-orgs, the level may change.  I'd like to automate a fix for that issue.

I have a function that will give me the correct portion of the query, in the format of
(l5deptcode=12345) 

Open in new window


There may be one or multiple instances of this criteria in the search filter.

I have the code that will search the filter for that data, pull out the department number, search for it and return the new department (if it changed).  Though I'm not convinced this is the best approach...  I may be 'changing' something that hasn't changed for example.

Here is what I'm using at the moment:
foreach ($li in $searcher.Filter.Split('(')) {
  if ($li.contains('deptcode')) {check-depts $li.split('=')[1].replace(')','')}
}

Open in new window


Ultimately I'd like to split the string and compare each department against where it currently sits and only replace it if it has been altered.  Perhaps a regex expert can chime in?

Example of a query

(&(|(estatus=A)(estatus=L))(|(&(eType=D)(&(resid=E*)(hiredDate<=2020-08-21)))(&(eType=C)(&(resid=N*)(hiredDate<=2020-08-21))))(|(l6deptcode=19431)(l7deptcode=61886)(l7deptcode=43172)(l8deptcode=76616)(l9deptcode=18406)))



Open in new window



Avatar of footech
footech
Flag of United States of America image

compare each department against where it currently sits
Can you clarify this?  What exactly are you (or would you like to) comparing (i.e what's the source of the item on each side of a comparison)?
How do we know something's been altered, and what exactly is altered?

This may be something of a start.  Using something like this, you get a list of the departments in the filter.
$filter = '(&(|(estatus=A)(estatus=L))(|(&(eType=D)(&(resid=E*)(hiredDate<=2020-08-21)))(&(eType=C)(&(resid=N*)(hiredDate<=2020-08-21))))(|(l6deptcode=19431)(l7deptcode=61886)(l7deptcode=43172)(l8deptcode=76616)(l9deptcode=18406)))'
[regex]::Matches($filter,"\(l\d+deptcode=\d+\)") | Select -ExpandProperty value

Open in new window

Avatar of sirbounty

ASKER

If I can strip out the L?deptcode numbers, I can pass them all through my function and have it return them in their new level (if it changed).  The function will return the query format.  So if I pass it "12345", it will return "(l8deptcode=12345)"
OK, so this
[regex]::Matches($searcher.filter,"\(l\d+deptcode=(?<dept>\d+)\)") | % { $_.Groups['dept'].Value }

Open in new window


really does the same thing as what you had before
foreach ($li in $searcher.Filter.Split('(')) {
  if ($li.contains('deptcode')) {check-depts $li.split('=')[1].replace(')','')}
}

Open in new window



To use regex to replace only those department numbers that have changed, you have to
 1) know what has changed
 2) search for the specific string (not just a pattern) that should be changed
 3) then change the value
Here's concept code.
$data = @"
dept,changed
19431,
61886,61800
43172,
76616,
18406,18300
"@ | ConvertFrom-Csv

$data.Where({"" -ne $_.changed}) | ForEach-Object {
    $searcher.Filter = $searcher.Filter -replace "(\(l\d+deptcode=)$($_.dept)(\))","`${1}$($_.changed)`${2}"
} -End { $searcher.Filter }

Open in new window


So this is really searching for (L?deptcode= followed by the specific number that we know has changed, followed by a closing parentheses.  I don't know if the number after the "L" has any importance to this process.
Here's a slightly different way of doing the search and replace (with the same result).
$data.Where({"" -ne $_.changed}) | ForEach-Object {
    $searcher.Filter = $searcher.Filter -replace "(?<=\(l\d+deptcode=)$($_.dept)(?=\))","$($_.changed)"
} -End { $searcher.Filter }

Open in new window

I think there's a touch of miscommunication here.
The department number will not change, but the level of that number may.
Using your regex above, I can display the new level using this:


foreach ($dept in [regex]::Matches($searcher.filter,"\(l\d+deptcode=\d+\)") | Select -ExpandProperty value) {
  $deptNo = $dept.split('=')[1].replace(')','')  #this would give me 81984
  $orglvl = $dept.replace('(','').replace(')','').split('=')[0]  #this would give me l5deptcode
  $newlvl = (Check-Depts $deptNo).replace(')','').replace('(','').split('=')[0] #this would give me l8deptcode


  if ($orglvl -ne $newlvl) {
    write-host "$deptNo $orglvl changed to $newlvl" -ForegroundColor Yellow
  } 

Open in new window

Results:
81984 l5deptcode changed to l8deptcode

Now I just need to plug that back in to the filter with the new level (l8deptcode=81984)
OK, yes that definitely changes my understanding.

Give this a shot.
foreach ( $dept in ([regex]::Matches($searcher.filter,"\(l\d+deptcode=(?<dept>\d+)\)")) ) {
    $newlvl = Check-Depts $dept.Groups['dept'].Value
    if ( $dept.Groups[0].Value -ne $newlvl ) {
        $searcher.Filter = $searcher.Filter -replace "$([regex]::Escape($dept.Groups[0].Value))","$newlvl"
    } 
}

Open in new window

I'm not getting Groups though?  
$dept is simply a string value - do I need to iterate through that regex differently?   
Tried this but it wipes out my filter... leaving just one '(l13deptcode=81921)' value without any of the filter leading or trailing that reference.
foreach ($dept in [regex]::Matches($searcher.filter,"\(l\d+deptcode=\d+\)") | Select -ExpandProperty value) {
  $deptNo = $dept.split('=')[1].replace(')','')
  $orglvl = $dept.replace('(','').replace(')','').split('=')[0]
  $newlvl = (Check-Depts $deptNo).replace(')','').replace('(','').split('=')[0]
  if ($orglvl -ne $newlvl) {
    $searcher.Filter = $searcher.Filter -replace $orglvl,$newlvl
  } 
}

Open in new window

In my last post, $dept is not just a string.
Sorry, but I believe I'm confused now. :\
ASKER CERTIFIED SOLUTION
Avatar of footech
footech
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I see.  My function simply returns a string however.  The function actually loops through all department levels to find out where the presence of that department number is, and when found, returns that single string (l3deptcode=xxxxx).
So I'm not sure how modify this from the group representation.
My function returns a string as well (and from what you've described, in the exact same format).  Try it (with the limited set of numbers it can handle) and tell me how the output differs.

As for the second code block, you should just be able to run it with no modification to update the query (filter) with the new L numbers.
Just to be clear: AD does not update the database if a value does not change. It's smarter than that.
Thanks Michael - I'm not updating AD (and I also cannot use adsisearcher).  I'm using ldap against a data view from a 3rd party product.

Apologies footech - I see where I was confused.
I think I have it working now.  I'll close this out shortly.  Thanks!
Thanks for the help!
Glad to help!