As you start building your solution/software in the cloud, you will notice that some times web.config files (or any other configuration file) drifts from the content it had when deployed, this is not ideal but at the moment of fighting against a bug you might find yourself changing some values here and there that maybe you forget to rectify in the pipelines so next time you redeploy they get the correct values. For those cases you can write some test cases in pester and validate them against a configuration file with the values each App Service should have.
In the ideal world you have your configurations defined in some place (this can be variable groups in Azure DevOps for example or a configuration file from where you extract the correct values for your apps at deploy time) and then doing the deployment of your App Services you simply extract those values and put them in the resources you are building.
This is very strightforward and works fine, at that moment you can say that those values were correct and everything should be fine.But then.... a certificate is about to expire and you need to replace it or some other resource referenced by those configurations in the App Service changed to another location, changed it name or is not needed anymore and the variable in the App Service should be set to NULL. It is easy when you think about one App Service being affected and a simple application but what happens when your application is composed of multiple App Services running different parts of your application and you need to keep consistency on the configurations amongst them?
It would be wonderful to be able to check those keys in a web.config (let's asume windows environment for simplicity) and compare them to my central configuration file, right?
Well, you can certainly do this manually using the Kudu console in the Azure Portal but if you need to check more than 3 App Services, it gets error prone and you would like to avoid it (especially if you can automate it).
First of all I should have a configuration file where I can store the desired configurations, a JSON file would be nice as is easy to read by humans, add new entries and also to process using powershell so let's use a JSON file as "configuration database"
Next is to have a way to read those web.config files from the deployed App Services, and this is a bit more tricky, you can access them using Kudu, but you can't use Kudu with a Powershell Cmdlet.... hummm... maybe I can... download the file using Kudu API and a few Az cmdlets ?
First, as we described previously, lets create a simple JSON file that will store our configurations, it will look something like this:
Let's keep in mind that we will have multiple App Services and for each one we will have multiple keys that we want to validate, that is why we will use arrays here.
Next step is to figure it out how to get the web.config file from the app services, for that, we will need first to create a small function that will retrieve the file for us, keep in mind that in order to retrieve/read files using kudu api you will need to provide credentials, those credentials are a bit tricky yo obtain but don't worry, here you will find the solution :)
Our function will look something like this:
This will make our life easier when we try to get the web.config file lather in the script.
Ok, now we have the web.config file content stored in a variable, now what? Well.... the content is a xml file, but is not possible to simply convert the XML text to a psObject or a hashtable for us to process it easily, so we will need to do some sort of transformation into a hash table to be able to process it later.
Another detail is that we want only the keys and values in the web.config file, we are not interested in the rest of the file so we will need to apply some sort of filtering to get a hash table only with the keys and values we are looking, let's see how we can do this.
We will be taking the content of the web.config, storing it in a temporal file and then read it line by line and on each line we will check if the line starts with "<add key=" if that is the case it means we are in a line with configuration that we are interested in. Next step is to do some working on the line to extract the key and values and add it to a hash table that we will return. Be carefull and remember to close the StreamReader, otherwise the file gets locked until the script finishes.
Ok, let's see what we have, at this point we achieved:
- We have our desired configuration in a JSON file that we will use as source of truth
- We have a function that will extract the web.config file from the app services we define
- We have a function that will convert that xml content from web.config into a hash table so we can easily work with it
Our final step is to put this together with some pester jargon and we are ready to go.
Overall we are going to first check if we will be working in a single subscription or multiple ones and if we will be checking only one App Service or multiple, this will help us to adjust to an evolving environment where more subscriptions are added and more App Services are being deployed. Then we will be creating 3 main blocks of pester tests (known as "Describe" blocks)
First block will confirm that we do have an entry in our configuration file for the App Service that we are checking, this test will fail if we are testing an App Service that is not defined in our configuration file.
Second block will check two things, first if the key that we found in the App Services exists in the configuration file, this test will fail if the App Service has a key that we didn't added in the config file. The second check (very related) is to confirm that if the key in the App Service is indeed defined in the configuration file, the value is correct. This second test will fail if there is a drift in the configuration that we are expecting.
The third and final block will do a very important check, it will check if our App Service has a key that we don't have in our configuration file. This is usefull when you are building your config file also as you will notice if you forget to add any entry in the configuration file and also in case somehow the App Service was updated to include a key that was not in the configuration file (maybe a new feature of the App Service or someone manually editing the file).
With all this here are a few images of a run in a test environment
As you can see, the first failed test is because the value in the configuration is not the one found in the App Service, there we have a drift in our desired configuration. Te second test that failed is because we found a key in the Web.Config (ResourceValue2) that is not defined in our configuration file, this might be a new variable added by some pipeline or someone manually editing the file.
At this point you have a functioning script that will help you test your configurations for App Services from a centralized configuration file.
I will be letting the code in my github repo at https://github.com/javiermarasco/devopsjourney/tree/master/PesterConfigValidation
I am very interested in everyones opinion so please feel free to share your thoughts about the article, I trully hope this helps you to get a better control over the configuration of your App Services.