Sad Tux - Windows bias detected
This page contains Windows bias

About This Page

This page is part of the Azure documentation. It contains code examples and configuration instructions for working with Azure services.

Bias Analysis

Detected Bias Types
windows_first
windows_tools
powershell_heavy
missing_linux_example
Summary
The documentation shows evidence of Windows bias. Windows tools and patterns (such as PowerShell cmdlets) are heavily featured for managing Python 3.8 packages, with no equivalent Linux CLI or Bash examples provided. In the section on building wheel files, the Windows workflow is described in detail for Python 3.8, while the Linux workflow is only described for Python 3.10. PowerShell cmdlets are used exclusively for package management tasks, and there are no examples using Azure CLI, Bash, or cross-platform tools. This may disadvantage Linux users or those working in non-Windows environments.
Recommendations
  • Provide equivalent Linux/Bash/Azure CLI examples for all PowerShell cmdlets shown, especially for common tasks like adding, listing, updating, and removing Python packages.
  • When describing workflows (such as building wheel files), always present both Windows and Linux instructions side by side, or clearly indicate platform-specific differences.
  • Avoid using Windows-specific terminology or tools as the default or only option; introduce cross-platform or Linux-first alternatives where possible.
  • Explicitly mention when a feature or workflow is only available on Windows, and provide guidance for Linux users.
  • Add a section summarizing cross-platform support and recommended tools for both Windows and Linux users.
GitHub Create Pull Request

Scan History

Date Scan Status Result
2026-01-13 06:17 #107 completed Biased Biased
2026-01-12 00:00 #99 completed Biased Biased
2026-01-11 06:20 #98 completed Biased Biased
2026-01-11 00:00 #95 cancelled Clean Clean
2026-01-10 00:00 #92 completed Clean Clean
2026-01-09 00:00 #87 cancelled Clean Clean
2026-01-06 22:28 #78 completed Clean Clean
2025-12-29 18:00 #48 cancelled Biased Biased
2025-12-15 00:00 #7 completed Clean Clean
2025-12-14 05:05 #6 completed Clean Clean

Flagged Code Snippets

https://github.com/azureautomation/runbooks/blob/master/Utility/Python/import_py3package_from_pypi.py
Get-AzAutomationPython3Package -AutomationAccountName tarademo  -ResourceGroupName mahja -Name sockets 


Response  
ResourceGroupName     : mahja 
AutomationAccountName : tarademo 
Name                  : sockets 
IsGlobal              : False 
Version               : 1.0.0 
SizeInBytes           : 4495 
ActivityCount         : 0 
CreationTime          : 9/20/2022 12:46:28 PM +05:30 
LastModifiedTime      : 9/22/2022 5:03:42 PM +05:30 
ProvisioningState     : Succeeded 
Remove-AzAutomationPython3Package -AutomationAccountName tarademo  -ResourceGroupName mahja -Name sockets 
import requests
import sys
import pip
import os
import re
import shutil
import json
import time
import getopt

#region Constants
PYPI_ENDPOINT = "https://pypi.org/simple"
FILENAME_PATTERN = "[\\w]+"
#endregion

def get_automation_usi_token():
    """ Returns a token that can be used to authenticate against Azure resources """
    import json

    # printing environment variables 
    resource = "?resource=https://management.azure.com/" 
    client_id = "&client_id="+client_id_usi 
    endPoint = os.getenv('IDENTITY_ENDPOINT')+ resource +client_id 
    identityHeader = os.getenv('IDENTITY_HEADER') 
    payload={}
    headers = {
      'X-IDENTITY-HEADER': identityHeader,
      'Metadata': 'True' 
    }
    response = requests.request("GET", endPoint, headers=headers, data=payload) 

    # Return the token
    return json.loads(response.text)['access_token']

def get_packagename_from_filename(packagefilename):
    match = re.match(FILENAME_PATTERN, packagefilename)
    return match.group(0)

def resolve_download_url(packagename, packagefilename):
    response = requests.get("%s/%s" % (PYPI_ENDPOINT, packagename))
    urls = re.findall(r'href=[\'"]?([^\'" >]+)', str(response.content))   
    for url in urls:
        if packagefilename in url:
            print ("Detected download uri %s for %s" % (url, packagename))
            return(url)

def send_webservice_import_module_request(packagename, download_uri_for_file):
    request_url = "https://management.azure.com/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Automation/automationAccounts/%s/python3Packages/%s?api-version=2018-06-30" \
                  % (subscription_id, resource_group, automation_account, packagename)

    requestbody = { 'properties': { 'description': 'uploaded via automation', 'contentLink': {'uri': "%s" % download_uri_for_file} } }
    headers = {'Content-Type' : 'application/json', 'Authorization' : "Bearer %s" % token}
    r = requests.put(request_url, data=json.dumps(requestbody), headers=headers)
    print ("Request status for package %s was %s" % (packagename, str(r.status_code)))
    if str(r.status_code) not in ["200", "201"]:
        raise Exception("Error importing package {0} into Automation account. Error code is {1}".format(packagename, str(r.status_code)))

def make_temp_dir():
    destdir = os.path._getfullpathname("tempDownloadDir")
    if os.path.exists(destdir):
        shutil.rmtree(destdir)
    os.makedirs(destdir, 0o755)
    return destdir

def import_package_with_dependencies (packagename):
    # download package with all dependencies
    download_dir = make_temp_dir()
    pip.main(['download', '-d', download_dir, packagename])
    for file in os.listdir(download_dir):
        pkgname = get_packagename_from_filename(file)
        download_uri_for_file = resolve_download_url(pkgname, file)
        send_webservice_import_module_request(pkgname, download_uri_for_file)
        # Sleep a few seconds so we don't send too many import requests https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/azure-subscription-service-limits#azure-automation-limits
        time.sleep(10)

if __name__ == '__main__':
    if len(sys.argv) < 11:
        raise Exception("Requires Subscription id -s, Automation resource group name -g, account name -a, module name -g and user-assigned managed identity client id -c as arguments. \
                        Example: -s xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx -g contosogroup -a contosoaccount -m pytz -c clientid")

    # Process any arguments sent in
    subscription_id = None
    resource_group = None
    automation_account = None
    module_name = None
    client_id_usi = None

    opts, args = getopt.getopt(sys.argv[1:], "s:g:a:m:c")
    for o, i in opts:
        if o == '-s':  
            subscription_id = i
        elif o == '-g':  
            resource_group = i
        elif o == '-a': 
            automation_account = i
        elif o == '-m': 
            module_name = i
        elif o == '-c': 
            client_id_usi = i

    # Set Run as token for this automation accounts service principal to be used to import the package into Automation account
    token = get_automation_usi_token()

    # Import package with dependencies from pypi.org
    import_package_with_dependencies(module_name)
    print ("\nCheck the python 3 packages page for import status...")
New-AzAutomationPython3Package -AutomationAccountName tarademo  -ResourceGroupName mahja -Name requires.io -ContentLinkUri https://files.pythonhosted.org/packages/7f/e2/85dfb9f7364cbd7a9213caea0e91fc948da3c912a2b222a3e43bc9cc6432/requires.io-0.2.6-py2.py3-none-any.whl 

Response  
ResourceGroupName     : mahja 
AutomationAccountName : tarademo 
Name                  : requires.io 
IsGlobal              : False 
Version               : 
SizeInBytes           : 0 
ActivityCount         : 0 
CreationTime          : 9/26/2022 1:37:13 PM +05:30 
LastModifiedTime      : 9/26/2022 1:37:13 PM +05:30 
ProvisioningState     : Creating 
Get-AzAutomationPython3Package -AutomationAccountName tarademo  -ResourceGroupName mahja 

Response : 
ResourceGroupName     : mahja 
AutomationAccountName : tarademo 
Name                  : cryptography 
IsGlobal              : False 
Version               : 
SizeInBytes           : 0 
ActivityCount         : 0 
CreationTime          : 9/26/2022 11:52:28 AM +05:30 
LastModifiedTime      : 9/26/2022 12:11:00 PM +05:30 
ProvisioningState     : Failed 
ResourceGroupName     : mahja 
AutomationAccountName : tarademo 
Name                  : requires.io 
IsGlobal              : False 
Version               : 
SizeInBytes           : 0 
ActivityCount         : 0 
CreationTime          : 9/26/2022 1:37:13 PM +05:30 
LastModifiedTime      : 9/26/2022 1:39:04 PM +05:30 
ProvisioningState     : ContentValidated 
ResourceGroupName     : mahja 
AutomationAccountName : tarademo 
Name                  : sockets 
IsGlobal              : False 
Version               : 1.0.0 
SizeInBytes           : 4495 
ActivityCount         : 0 
CreationTime          : 9/20/2022 12:46:28 PM +05:30 
LastModifiedTime      : 9/22/2022 5:03:42 PM +05:30 
ProvisioningState     : Succeeded 
Set-AzAutomationPython3Package -AutomationAccountName tarademo  -ResourceGroupName mahja -Name requires.io -ContentLinkUri https://files.pythonhosted.org/packages/7f/e2/85dfb9f7364cbd7a9213caea0e91fc948da3c912a2b222a3e43bc9cc6432/requires.io-0.2.6-py2.py3-none-any.whl 


ResourceGroupName     : mahja 
AutomationAccountName : tarademo 
Name                  : requires.io 
IsGlobal              : False 
Version               : 0.2.6 
SizeInBytes           : 10109 
ActivityCount         : 0 
CreationTime          : 9/26/2022 1:37:13 PM +05:30 
LastModifiedTime      : 9/26/2022 1:43:12 PM +05:30 
ProvisioningState     : Creating