How to Get MSSQL Server Download Links
What do you mean? The links to MSSQL Server installers are easy to find.
Yes, those links are out there:
SQL Server | Link |
---|---|
SQL Server 2017 Developer Edition | https://go.microsoft.com/fwlink/?linkid=853016 |
SQL Server 2019 Developer Edition | https://go.microsoft.com/fwlink/?linkid=866662 |
SQL Server 2022 Developer Edition | https://go.microsoft.com/fwlink/?linkid=2215158 |
But the download is a small 5MB exe file. When you run it, it downloads a 1.5GB "proper" installer file.
This is a problem if you automate things, install SQL server on many machines, or look for the installer in a language different from English.
However, the EXE binary file contains links to 100 different installers, which can be extracted with a relatively simple Python script.
TL;DR
Stop this yada yada yada, I just want the links.
Follow the Google Colab Python notebook.
Or download the Python notebook and run it yourself.
Tell me how it works
Let's dive into the Python dojo with kata-kata sticks.
First, there is an AsciiTextExtractor. This helper class converts binary exe files to ASCII text and allows to search for content starting and ending with a specific text. This is used to search for XML opening and closing tags.
class AsciiTextExtractor:
def __init__(self, content, beginTxt, endTxt):
self.__beginTxt = beginTxt
self.__endTxt = endTxt
filteredContent = bytearray(
filter(lambda x: x == 10 or (x >= 32 and x < 127), content)
)
self.__contentTxt = filteredContent.decode("ascii")
def __iter__(self):
self.__idxEnd = 0
self.__idxBegin = 0
return self
def __next__(self):
if self.__idxBegin == -1:
raise StopIteration
self.__idxBegin = self.__contentTxt.find(self.__beginTxt, self.__idxEnd)
if self.__idxBegin == -1:
raise StopIteration
self.__idxEnd = self.__contentTxt.find(
self.__endTxt, self.__idxBegin + len(self.__beginTxt)
)
if self.__idxEnd == -1:
raise StopIteration
return self.__contentTxt[self.__idxBegin : self.__idxEnd + len(self.__endTxt)]
MSSQL Installer exe file has many little XML resources embedded, one for each product and language.
<Manifest xmlns="http://schemas.datacontract.org/2004/07/InstallerEngine" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<DownloadFilesList>
<ReliableFile>
<DownloadRoot>https://download.microsoft.com/download/6/d/f/6df2ef5b-235c-4b38-89cb-d3d98797dbfc</DownloadRoot>
<FileHash>e59b53b4e7a79d254bd15c6dc27c9d3bf97cd64c</FileHash>
<FileName>SQLServer2022-DEV-x64-DEU.box</FileName>
</ReliableFile>
</DownloadFilesList>
</Manifest>
GetMsDownloadLinks function downloads MS SQL exe installer file (around 6 Mbytes) and searches for all those pesky XMLs.
Then, it parses each XML to extract download URLs and SHA1 hashes.
import requests
from xml.dom.minidom import parseString
def GetMsDownloadLinks(msInstallerExeUrl):
print(f"Requesting {msInstallerExeUrl}")
request = requests.get(msInstallerExeUrl, allow_redirects=True)
print(f"Downloaded {request.url}")
beginTxt = '<Manifest xmlns="http://schemas.datacontract.org/2004/07/InstallerEngine" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">'
endTxt = "</Manifest>"
for txt in AsciiTextExtractor(request.content, beginTxt, endTxt):
xml = parseString(txt)
reliableFiles = xml.getElementsByTagName("ReliableFile")
for reliableFile in reliableFiles:
downloadRoot = reliableFile.getElementsByTagName("DownloadRoot")
fileName = reliableFile.getElementsByTagName("FileName")
fileHash = reliableFile.getElementsByTagName("FileHash")
if downloadRoot and fileName and fileHash:
downloadUrl = f"{downloadRoot[0].firstChild.nodeValue}/{fileName[0].firstChild.nodeValue}"
sha1Hash = f"{fileHash[0].firstChild.nodeValue}"
yield (downloadUrl, sha1Hash)
GetMsSqlDownloadLinks is a wrapper around GetMsDownloadLinks. It knows the links to different SQL EXE bootstrappers, removes duplicates and sorts the results. It also allows filtering, e.g. for specific languages or editions.
import os
sqlExelinks = {
2017: "https://go.microsoft.com/fwlink/?linkid=853016", # SQL Server 2017
2019: "https://go.microsoft.com/fwlink/?linkid=866662", # SQL Server 2019
2022: "https://go.microsoft.com/fwlink/?linkid=2215158", # SQL Server 2022
}
# supported versions: 2022, 2019, 2017
def GetMsSqlDownloadLinks(version=2022, searchFilter=""):
sqlExeUrl = sqlExelinks[version]
# add links to the set to get rid of duplicates
msDownloadLinks = set(GetMsDownloadLinks(sqlExeUrl))
# add links to the to list, to be able to sort
sortedMsDownloadList = list(msDownloadLinks)
# sort by filename (x[0] is url)
sortedMsDownloadList.sort(key=lambda x: os.path.basename(x[0]))
return filter(lambda x: searchFilter in x[0], sortedMsDownloadList)
Let's use all that heavy lifting to print links to the english versions of MSSQL Server 2022 installer.
for link, sha1 in GetMsSqlDownloadLinks(2022, "ENU"):
print(f"{link} {sha1=}")
Even thought the exe bootstrapper is for the Developer edition of SQL Server, it contains the links to SQL Express, iso image and a few others.
Vielen Dank, Python 🐍