One of our current projects is a multitenant system. Recently we’ve got a requirement to change IDs of our tenants from hard to guess random cryptic (eg. ‘adfx65sd’) to natural ones. There are two areas for changes: database and filesystem. The database is handled with EF migration rather nicely.

However filesystem is slightly more complicated. We have those IDs being spread accross different folders, i.e. there are tenant specific styles, images and so on. How to perform rename in some smart way?

First we need to find items that should be renamed. It is easy to do even with Windows File Explorer (open the folder with your source files and just search by tenant ID). You can go further and rename stuff just in search results. But that is… no fun at all. Files and folders have different naming schemas, and we have more than one tenant, so this would become boring and error prone.

Remember that we are developers and our passion is to write the code. So, lets solve this with code! Our choice is a Powershell, because why not? I posted once how it can be used to parse files, make sure to read it, some Powershell mechanics used below is explained there.

Finding files that should be renamed is rather easy:

ls *old_tenant_id* -Recurse # ls is just an alias for Get-ChildItem

We should get list of files and folders that are subject to rename. What to do next?

Powershell provides lot of useful stuff to work with files. Our today’s hero is Rename-Item. It has two arguments, what should be renamed and what new name should it get. Previous ls gives us that first one. What about new name?

If I was using C# I would try String.Replace(). I am not sure if Powershell has anything like that, and I don’t care, to be honest. Remember that Powershell is build on top of .NET. And it does mean 2 things:

  1. Everything is an object.
  2. .NET APIs are available.

It means that we can use Name property of each file / folder, and since it’s type is a String, we can also use String.Replace().

Let’s confirm our theory first:

ls *old_tenant_id* -Recurse `
  | % { $newName = $_.Name.Replace('old_tenant_id', 'new_tenant_id'); `
        write-host "$_ -> $newName" }

This should print all files / folders with their new names. If everything is fine, we can proceed with renaming:

ls *old_tenant_id* -Recurse `
  | % { 
      Rename-Item -path $_ -NewName $_.Name.Replace('old_tenant_id', 'new_tenant_id') 
  }

It does the trick! Now all our stuff is renamed and ready to be committed.

But that’s not all. Remember, we have many tenants. Sure, we can go ahead and just update previous script and run it for every tenant. However what if we could do it once for all?

The solution is rather simple. Just add all old and new IDs for each tenant to a dictionary and iterate over it. Here is the script:

@{'oldtenant1_key'='newtenant1_key';'oldtenant1_key'='newtenant1_key'} `
  .GetEnumerator() ` # this is needed by %{} 
 | % { 
   $kvp = $_; ` # because $_ will be shadowed in the inner loop
   ls "*$($kvp.Key)*" -Recurse  `
        | % { Rename-Item -path $_ -NewName `
            $_.Name.Replace($kvp.Key, $kvp.Value) -Force }}

And finally all our assets got renamed by this simple (almost) oneliner.

Summary

Powershell is simple and powerfull tool. Next time when you’ll need to do something tedious, just try to use Powershell. If you don’t know it rather well – ping me, I would be glad to help.

Happy coding!