1

I've inherited a deployment script that reads through files with specific names and extensions deployed to our on-prem web servers, looks line-by-line in those text files for string matches that signify they need replacing, and then inserts, in this case, the correct database and catalog names for the specific server being deployed to.

Unfortunately, though the script works fine On My Computer (c)1984, it's not working when run by TFS as part of the deploy process.

I've added an embarrassing amount of debug info, but haven't been able to track down the issue.

Code

Here's the pertinent code:1

$getFilesParams = @{
    Path = $SearchDirectory
    Include = @(
      "*.asa", "*.asp",
      "*.aspx", "*.config",
      "*.inc", "*.ini",
      "appsettings.json"
    )
    Exclude = "Web.*.config"
    Recurse = $True
}

$files = @(Get-ChildItem @getFilesParams)
foreach ($f in $files) {


# DEBUG STARTS HERE
  Write-Feedback "`tFile to search for Data Source: $f" -LogFile "$LogFile"

  if ($f.Name -eq "appsettings.json") {
    try {
      Write-Feedback "`t`tFound the appsettings.json file: $($f.Length) $($f.LastWriteTime) $($f.Name)" -LogFile $LogFile

      Get-Content $f | % {
        Write-Feedback "!!!! Line: $_"
      }

      Select-String $f -Pattern "Data Source" | % { Write-Feedback "`t`t`tFound data source: $_" }

      # was suspicious it'd work the first but not the second time. No, it fails each time I Get-Content.
      Get-Content $f | % {
        Write-Feedback "@@@@ Line: $_"
      }
    }
    catch {
      Write-Feedback "An error occurred with appsettings.json:" -LogFile $LogFile
      Write-Feedback $_ -LogFile $LogFile
    }
  }
# DEBUG ENDS


}

$files = $files |
  Select-String -Pattern "Data Source" |
  Group-Object path |
  Select-Object name

$count = $files.count
if ($count)
{
  @"
Found $count files....
Processing:
"@ | Write-Feedback -LogFile $LogFile

  # etc etc he fixes the cable
}
else
{
  '  Did not find any databases catalogs!' | Write-Feedback -LogFile $LogFile
}

Then we go line by line through the files in $files. The problem is that my appsettings.json file, which does contain Data Source (okay, it's lowercase right now -- data source), doesn't get captured and no replacements are made.

Note: Write-Feedback is a convenience function that writes to the console (Write-Host) and to a file, if one is given.

Local output

When I run locally, I get what I'm expecting (edited a bit for brevity):

File to search for Data Source: C:\path\appsettings.json
    Found the appsettings.json file: 993 01/12/2022 13:04:52 appsettings.json
!!!! Line:  "SomeJsonKey": "data source=localhost;initial catalog=SomeDb;integrated security=True",
        Found datasource: C:\path\appsettings.json:9:  "SomeJsonKey": "data source=localhost;initial catalog=SomeDb;integrated security=True",
@@@@ Line:  "SomeJsonKey": "data source=localhost;initial catalog=SomeDb;integrated security=True",
Found 1 files....
Processing:
  C:\path\appsettings.json....

Production output

But when it's run as part of the deployment, I get...

File to search for Data Source: E:\path\appsettings.json
    Found the appsettings.json file: 762 01/14/2022 15:15:02 appsettings.json
Did not find any databases catalogs!

So it sees appsettings.json , it even knows appsettings.json has a length (they are different files, so the two lengths here aren't an issue), but it won't Get-Content, much less find the line with Data Source in it.

Notepad++ says the file is ANSI, which is fine, I think. There're no extended characters, so that's the same as UTF8, which is what I expected. Don't think that'd break Get-Content.

To be clear, Not necessarily looking for an answer. Just wondering what my next debug(s) step would be.

I've looked up a little about permissions, though I'm not sure how I'd tell which entry from (Get-Acl $Outfile).Access represents the process that's currently running. But I also would expect an exception of some kind if it can't read the file.


Just for fun, here's a picture of the release UI for the TFS server this is running on. If you've seen it before, you'll get it.

I was hoping I could figure this out through straight PowerShell, but I can check things out there to a degree if that's useful. That said, I don't have full perms on the TFS site. I probably have more running as... whoever I'm running as when the PowerShell (third step) is executed.

the current release UI from TFS with three steps pictured: grab the zip with the PowerShell, unzip the PowerShell, run it.

(sorry for all the redaction; almost certainly not necessary, but better safe than sorry (sorrier?), etc?)


1 I realize this code can be optimized. Please ignore that; it's, again, inherited, and I'm trying to get it to work with a minimum of churn.

ruffin
  • 16,507
  • 9
  • 88
  • 138
  • Wait, I'm confused a bit. How exactly is it being ran when it doesn't work? I.e: running it from a service account, or is it being invoked remotely, etc.? – Abraham Zinala Jan 14 '22 at 22:25
  • 1
    Change `Get-Content $f` to `$f |Get-Content` (or `Get-Content $f.FullName`) – Mathias R. Jessen Jan 14 '22 at 22:27
  • @AbrahamZinala That's a good question -- it's an out of date version on on-premise Team Foundation Server. I'll add a quick picture of the web UI to the question so it'll click if you've seen one before. That's one of my questions: How would I determine "who I am" while running it? I have access to the log files on the server to which it's being deployed after it's done, and complete control over the PS script, but, as I mentioned with the permissions stuff, I really don't know which access entry represents the current process. – ruffin Jan 14 '22 at 22:32
  • @MathiasR.Jessen I'll give that a shot, but am curious why that would work. I'm testing both in `pwsh` via VS Code _and_ PS ISE (so PS 5), and it's working locally as written. :confused: – ruffin Jan 14 '22 at 22:43
  • 1
    @ruffin `Get-Content $f` resolves to `Get-Content -Path appsettings.json`, (not `Get-Content -Path E:\path\appsettings.json`) so it will only work if `$f` is located in the current directory. I assume it works on your machine because you're running the script from `C:\path`, so `appsettings.json` is a valid relative path, but the script on the remote box probably has a different working directory. – Mathias R. Jessen Jan 14 '22 at 22:48
  • @MathiasR.Jessen No dice. Gave the same output -- finds file and sees length, but no line by line output. I like how you're thinking, though. I hadn't thought about the runtime context being different and potentially throwing things off. I'll stew on that for a minute. – ruffin Jan 14 '22 at 22:57
  • Try `Write-Feedback ([Security.Principal.windowsIdentity]::GetCurrent().Name)` to see who the process is running as. – user2871239 Jan 15 '22 at 08:24
  • `Get-Content` throws non-terminating errors in the event of `Access is Denied` or `Cannot find path` (e.g. because access to the path is denied) so your wrapper try-catch won't be seeing them if they're occurring. Add `-ErrorAction Stop` to your `Get-Content` statements if you want to catch them. – user2871239 Jan 15 '22 at 08:36

0 Answers0