Monday, December 10, 2012

"Clever" uninstall of msi packages/applications using PowerShell

When I created my own PowerShell script library for BizTalk deployment automation I ran across the need to uninstall applications, both BizTalk applications and non-BizTalk ones, by only knowing their name.

At first, I solved it using a WMI query like so:
$name = "application name"
$product = Get-WmiObject -Class Win32_Product -Filter "name='$name'" -ComputerName "localhost"
[void]$product.Uninstall()

This is not a recommended way of doing it though. It is both very slow and also causes a bit of spamming in the Windows Eventlog since the query in fact does a reconfigure of ALL applications installed. This reconfiguration can also cause a bit of other issues in some cases.

I then created another way of trying to uninstall applications in a more failsafe and secure way by using msiexec with the uninstall flag. The tricky part was to find a way to get the product key in order to be able to use msiexec since it requires this for uninstalling an application. The result can be found below.

The script function will take the application name as argument. It will then via the registry (note that this is configured for x64, so change the path if you are running x86) look up the application settings. If the application can be found via name (it should), we extract the product key. Then this is used as a parameter to msiexec.

If the product key cannot be found (it happens), we will instead try to read the uninstall string that is set when installing the application. Windows will run this string when you choose to uninstall an application, so why do not we use it? If found, we extract the product key and do our msiexec call.

If all fail, we throw an exception to be caught in the real part of the script.

This is the full script function:

Function Uninstall-Program([string]$name)
{
    $success = $false

    # Read installation information from the registry
    $registryLocation = Get-ChildItem "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\"
    foreach ($registryItem in $registryLocation)
    {
        # If we get a match on the application name
        if ((Get-itemproperty $registryItem.PSPath).DisplayName -eq $name)
        {
            # Get the product code if possible
            $productCode = (Get-itemproperty $registryItem.PSPath).ProductCode
           
            # If a product code is available, uninstall using it
            if ([string]::IsNullOrEmpty($productCode) -eq $false)
            {
                Write-Host "Uninstalling $name, ProductCode:$code"
           
                $args="/uninstall $code"

                [diagnostics.process]::start("msiexec", $args).WaitForExit()
               
                $success = $true
            }
            # If there is no product code, try to read the uninstall string
            else
            {
                $uninstallString = (Get-itemproperty $registryItem.PSPath).UninstallString
               
                if ([string]::IsNullOrEmpty($uninstallString) -eq $false)
                {
                    # Grab the product key and create an argument string
                    $match = [RegEx]::Match($uninstallString, "{.*?}")
                    $args = "/x $($match.Value) /qb"

                    [diagnostics.process]::start("msiexec", $args).WaitForExit()
                   
                    $success = $true
                }
                else { throw "Unable to uninstall $name" }
            }
        }
    }
   
    if ($success -eq $false)
    { throw "Unable to find application $name" }
}

Sunday, December 9, 2012

Running an Integration Competence Center for small businesses

A while ago I saw a post from an old colleague on LinkedIn where he linked to an old article called Too small for an Integration Competency Center? Think again. I was intrigued to read the article since I am in the middle of establishing an ICC at the organization I'm at, and we are quite small in that regard.

The four building blocks involved are:
  • Adopt common integration practices
  • Standardize on a common integration platform
  • Create a decentralized group that specializes in integration
  • Form a centralized ICC
This has all been done at the organization I am at, with the experience I have from larger organization as a basis for the setup. In my case, with an IT department of a whopping four people, forming an ICC is no big challenge in that regard.

I have opted to include external parties in the ICC to form a team that is not bound to a strict organizational hierarchy, but rather be agile and flexible to the demands from the organization at stake.

The basis of the ICC is in my role as integration architect/supervisor in the IT department. Coupled to this I have included an external integration architect to have a form of sounding board. Most, if not all, integration development work is carried out by consultants which the external integration architect is responsible to coordinate. As needed, I also include internal resources in the ICC to help forming the ICC and drive the integration development forward. So basically, the ICC is in it's simple form just two persons, with several others coming and going as needed.

So far, this is working very well. The agility in the ICC is a strong factor to consider. A lot of times, people are included full time even if they are not needed full time. Since the external architect is contracted on an as-needed-basis, costs can be brought down. Of course, that puts a lot of burden on me.

However, I have strived to create a simple and effective process of the entire integration life cycle. Document templates are available for everything from requesting an integration (done by the organization) to detailed specification and testing of the completed development. The development guidelines and deliverance specifications are to be as standardized as possible to not bind anything to a single person or organization (except our own). I aim to be able to both replace the consultants involved as well as myself without having to explain anything about the setup and behavior of the ICC and involved parts.

As for the four building blocks above, I feel that all (almost) of them can be ticked off in our case. The platform is BizTalk, best practices are established through documentation and processes based on experience from me and consultants, a decentralized group is formed and the ICC is formed and handling most of the integration needs. What is left is the funding that is still divided and handled by separate organizational units rather than taken care of completely by a central organization. This is currently the biggest issue to handle since it hamper a true value-for-money path regarding integration.

But to summarize, in accordance to the article linked in the beginning of this post, you cannot have a too small of an organization for an ICC to be formed. By including resources on a need-to basis as well as including external consultants where needed, it can be handled in a very effective way. The important part is to have a clear process of the entire life cycle as well as clear boundaries between different parties involved. My role in the whole ICC is most importantly to coordinate the different tasks between the resources, and as long as that is done appropriately, everything is flowing smoothly. Having a centralized funding of the work is not necessary, but very (very!) preferable.

Sunday, December 2, 2012

Simple dos and don'ts for versioning webservice interfaces (and similar integration interfaces)

Earlier this week I went to a user group gathering for the ERP system we utilize at work. The topic for the day was "integration". At the end of the day during an open Q and A session, I approached the ERP representatives with questions on how they worked with the web service API exposed from the ERP system. I asked them if using the current web services guaranteed that they would work without changes and need for deep regression testing when upgrading the system. I also asked them how new functionality to existing services would be implemented, from an integration point of view.

The answer was that current implementations would require normal regression testing after upgrading, but that there could be changes in the backend making the services work a bit different in the new versions, depending on the changes made in the business logic and GUI.

This made me a bit quezy and fidgety. In the best of worlds, I want a web service to be seen as a hard written contract between two parties, in this case the ERP system on one side and the integration platform on the other. By exposing a service, you should guarantee that the specification for the service is not to be changed and that the underlying logic is to be set in stone as well. That seemed to not be the case.

The discussion then evolved into the realm of versioning and planned changes of existing services. As of now, new functionality is added to the service without changing its name, version or namespace. Fields are simply added as optional.

This method is something I have several issues with, and which I also expressed at the session (maybe a bit too harsh, but it is an important subject to me). First off, by adding new functionality as optional fields, you will break one of the (many) benefits with web service contracts: hard typing of them! By doing it this way, the developers will make future maintenance of the services harder, for both themselves as well as those utilizing the services. Also, the clients will have to upgrade the integrations whether they want to or not as soon as the ERP system is to be upgraded and the services change.

I instead proposed that they should version their services and operations. When changing an operation, create a new version of it and as long as it is possible, make sure that the previous version(s) are still functional. If a service operation is to be removed from the system, mark it as obsolete and let the clients know which version of the ERP system it will be removed in, and preferably how to utilize other services and operations to enable the needed functionality. I myself think that this is a very obvious way of handling the dilemma. I have grown to understand that it is very rare that versioning is used though and I do my best preaching the above statements.

So please, make sure that you use some form of versioning strategy when building your interfaces. It will make my (and many others, I guarantee) life a lot easier.