Getting version of a file without a version property

Hello,

I am trying to gather the version info from a file which does not have a version property. I used Orca to see what properties are within the MSI and I can see “ProductVersion” but still I am not able to get results from BigFix.

I plan to use the version contained in the Subject property but I’m not sure if it is possible as I do not see any results when using version block of file.

Wondering if anyone encountered a same scenario and was able to successfully collect version info from other properties?

I don’t think you’ll be able to get the MSI version using relevance. An MSI doesn’t have file version as its really a container, not too different to the concept of a zip file, though it contains compressed binaries as well as install database info that allows the windows installer service to process the file.

If you need to get a product version of the file within the MSI, you probably need to have a script parse the MSI, extract the product version, which is what you are seeing with Orca, then output the result to a file that can then be parsed via a property

2 Likes

I found a PowerShell function I pulled from the internet many years ago (circa 2009) that will parse the product version from an MSI.

Calling the function and passing the MSI path should return the version of the product, e.g. Get-VersionFromMSI "C:\TEMP\Orca.Msi"

Function code

 Function Get-VersionFromMSI() {
    <#
    .SYNOPSIS
        This function retrieves properties from a Windows Installer MSI database. 
    .DESCRIPTION 
    This function uses the WindowInstaller COM object to pull all values from the Property table from a MSI 
    .EXAMPLE 
    Get-MsiDatabaseProperties 'MSI_PATH' 
    .PARAMETER FilePath 
    The path to the MSI you'd like to query 
    #> 
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$True,
        HelpMessage='What is the path of the MSI you would like to query?')]
        $FilePath
        )
    begin 
        {
        $WindowsInstaller = New-Object -com WindowsInstaller.Installer
        }

    process 
        {
    if(!(test-path $FilePath)){
        throw "$FilePath not exist"
        exit
    }
    try 
        {
        $Database = $WindowsInstaller.GetType().InvokeMember(“OpenDatabase”, “InvokeMethod”, $Null, $WindowsInstaller, @($FilePath,0))
        $View = $Database.GetType().InvokeMember(“OpenView”, “InvokeMethod”, $Null, $Database, (“SELECT Value FROM Property WHERE Property = 'ProductVersion'”))
        $View.GetType().InvokeMember(“Execute”, “InvokeMethod”, $Null, $View, $Null)
        $record = $View.GetType().InvokeMember( "Fetch", "InvokeMethod", $Null, $View, $Null )
        $version = $record.GetType().InvokeMember( "StringData", "GetProperty", $Null, $record, 1 )
        return $FilePath + " = " + $version
        }
    catch
        {
        throw "Failed to read the MSI version number" -f $_
        }
    }
}
2 Likes

Thanks a lot for leading me to the right direction, @SLB! Will look into this.

2 Likes

I’m a little confused why you are trying to use relevance to examine inside an MSI. What is the goal?

It might be possible to get this from the Subject property using relevance, but I’m not certain.

MSI files are actually an OLE database format that is the same as the older Microsoft Word “.doc” format, or Excel “.xls” format, but not the same as the newer “.docx” or “.xlsx” format.

You can absolutely do this with Python or PowerShell or similar.

I have a VBScript that can do it here: tools/VBS/msiGetProperty.vbs at master · jgstew/tools · GitHub

I have an outdated Python2 script that can do it (and a lot more) here: generate_bes_from_template/src/generate_bes_from_template/MSI_to_BES.py at master · jgstew/generate_bes_from_template · GitHub

Still unsure what the goal is of trying to get this info with Relevance though. Generally you would inspect the Windows Registry for the DisplayVersion of an installed software product, not inspect the version within an MSI file.

The vendor is releasing MSI updates for their agents via a specific URL (similar to Chrome…). However, we always have to check the releases page and update the relevance on our custom content for the version checks (current version installed vs latest one available).

We are trying to automate deployment of agent updates by checking if the version of the MSI from the URL is greater than what’s currently installed, run update commands. If not, do nothing and delete files.

To be able to do this via relevance would be better than running a PowerShell script and reading the output from a file. :slight_smile:

Does the vendor make a download API available for the agents or perhaps a release info page that you can parse?
I have done similar projects and none of them parse the msi files directly instead I either read the release info page or use an API to collect data about the current release. I use an external automation orchestration tool(Jenkins +Python Scripts) for that and at the same time I generate new Fixlets in automated way once a new release is found using the BigFix REST API in this way no manual work is done at all.

1 Like

Thanks for the inputs @fermt! At this time, I do not have access and knowledge on Jenkins to fully automate this process. But this is something I’m interested in and hopefully be able to learn and create fully automated stuff. :slight_smile:

1 Like

Hi @anademayo !

Can you please provide link for the release page?

You can create similar automation with other tools like : PowerShell , Python etc…

2 Likes

Can you provide the URL / example?

I would still recommend doing something like this in python or powershell, but then use that to update a fixlet template and import it into BigFix using the REST API. This is basically what my MSI_to_BES script does but with MSIs that are already on the file system.

Even if you could do this directly in relevance, you’d still have to have something that runs the relevance automation periodically which would probably be written in something else. So that is why I’m still a little confused about what your goal is and how it would work. Do you have other examples or more info on what you have working already?

It seems like this is the URL? https://s3.amazonaws.com/ddagent-windows-stable/datadog-agent-7-latest.amd64.msi

From here? Basic Agent Usage for Windows

Maybe you dont need to that level of automation, or MSi version inspection. I did a Google (other search engines are available) on the file name in your screen cap, and assuming its for the DataDog agent, looking at Basic Agent Usage for Windows they provide a reference list for specific version and associated URLs for the agent installer so maybe you can use the version URL for your fixlets as opposed to a “latest version” URL?

https://ddagent-windows-stable.s3.amazonaws.com/installers_v2.json

EDIT: And now I see that @jgstew did the same sleuthing :smiley:

This is interesting, but why do some versions have “ddagent-cli” and others have both that and “datadog-agent”? Not sure why that is.

Seems like this is the newest agent download? https://s3.amazonaws.com/ddagent-windows-stable/datadog-agent-7.49.1-1.x86_64.msi

All that said, this does seem like something that could be added to Updates for Windows Applications Extended.

2 Likes

I would hazzard a guess that maybe thats how they differentiate between x86 and x64 agents which seemed to start at from the 7.46.0-1 agent?

   "7.45.1-1": {
  "x86_64": {
    "url": "https://s3.amazonaws.com/ddagent-windows-stable/ddagent-cli-7.45.1.msi"
  }
},
"7.46.0-1": {
  "x86_64": {
    "url": "https://s3.amazonaws.com/ddagent-windows-stable/ddagent-cli-7.46.0.msi",
    "url_ng": "https://s3.amazonaws.com/ddagent-windows-stable/datadog-agent-7.46.0-1.x86_64.msi"
  }
},
"7.47.0-1": {
  "x86_64": {
    "url": "https://s3.amazonaws.com/ddagent-windows-stable/ddagent-cli-7.47.0.msi",
    "url_ng": "https://s3.amazonaws.com/ddagent-windows-stable/datadog-agent-7.47.0-1.x86_64.msi"
  }
},

Usind a sample version and both file are the same SHA so guessing its more around vendor specific processes.

Q: (sha256 of it, name of it) of files ("ddagent-cli-6.47.1.msi";"datadog-agent-6.47.1-1.x86_64.msi") of folder "D:\Downloads"
A: 942e167ae30917b19b9c768c428a0e2d36b773730f4725df0e0b5d7fda1dcf3d, ddagent-cli-6.47.1.msi
A: 942e167ae30917b19b9c768c428a0e2d36b773730f4725df0e0b5d7fda1dcf3d, datadog-agent-6.47.1-1.x86_64.msi
1 Like

I just checked the newest one listed for both and they do appear to be exactly the same MSI with different names:

Q: sha256s of files ("datadog-agent-7.49.1-1.x86_64.msi";"ddagent-cli-7.49.1.msi")
A: 2900d9e319c1e410e43d00b72625bba819c6eec97aa0b12a62ca5fbb87dc926f
A: 2900d9e319c1e410e43d00b72625bba819c6eec97aa0b12a62ca5fbb87dc926f
T: 715.773 ms
I: plural string

@orbiton @jgstew @SLB Thank you all!

This is the only release page I know: Releases · DataDog/datadog-agent · GitHub

You are right, this is the URL they provide for the latest version. The latest agent version right now is 7.50.3 based on the MSI Subject property.

This would be great, however, there are other configuration considerations as it could overwrite folder/service permissions during updates when only default update parameters are used.

Hmm, that would be annoying. I would expect on upgrade if no parameters are used then existing config would stay intact.

Can you provide anonymized examples of parameters? Do you have an existing fixlet template?

Here’s a sample upgrade command where username and password for the service account is required:

msiexec /i ddagent.msi DDAGENTUSER_NAME=<USERNAME> DDAGENTUSER_PASSWORD=<PASSWORD>

Reference: Datadog Windows Agent User

Most times, we don’t need this parameters for regular servers. However, this is needed when SQL / DC servers are targeted.

1 Like

Is there a good way to know when this is required to exclude them from an update fixlet that would update the others?

Would it be just to detect that the service is not running under SYSTEM?

By default, Datadog Agent just uses “ddagentuser” account. This user gets created during install.
image

When there are special permissions on the server, the Datadog Agent service runs as a specific service account.

I hope that clarified your question.

1 Like