diff --git a/src/dotnet-install.sh b/src/dotnet-install.sh
index 42c201af4c..9400783829 100644
--- a/src/dotnet-install.sh
+++ b/src/dotnet-install.sh
@@ -638,7 +638,7 @@ get_version_from_latestversion_file() {
 
 # args:
 # json_file - $1
-parse_globaljson_file_for_version() {
+parse_globaljson_file() {
     eval $invocation
 
     local json_file="$1"
@@ -657,31 +657,84 @@ parse_globaljson_file_for_version() {
     sdk_list=${sdk_list//[\" ]/}
     sdk_list=${sdk_list//,/$'\n'}
 
-    local version_info=""
     while read -r line; do
       IFS=:
       while read -r key value; do
         if [[ "$key" == "version" ]]; then
-          version_info=$value
+          globaljson_version=$value
+        elif [[ "$key" == "rollForward" ]]; then
+          globaljson_roll_forward=$value
         fi
       done <<< "$line"
     done <<< "$sdk_list"
-    if [ -z "$version_info" ]; then
-        say_err "Unable to find the SDK:version node in \`$json_file\`"
-        return 1
-    fi
 
     unset IFS;
-    echo "$version_info"
     return 0
 }
 
+process_globaljson_file() {
+    if [[ -n "$json_file" ]]; then
+        globaljson_version=""
+        globaljson_roll_forward=""
+        parse_globaljson_file "$json_file" || return 1
+
+        # https://learn.microsoft.com/en-us/dotnet/core/tools/global-json#matching-rules
+        if [[ -z "$globaljson_roll_forward" ]]; then
+            if [[ -z "$globaljson_version" ]]; then
+                globaljson_roll_forward="latestMajor"
+            else
+                globaljson_roll_forward="latestPatch"
+            fi
+        fi
+
+        if [[ -n "$globaljson_version" ]]; then
+            IFS='.'
+            read -ra parts <<<"$globaljson_version"
+            unset IFS
+
+            local major="${parts[0]}"
+            local minor="${parts[1]}"
+            local featureBand="${parts[2]:0:1}"
+        fi
+
+        case "$globaljson_roll_forward" in
+        disable|major|minor|feature|patch)
+            version="$globaljson_version";
+            ;;
+
+        latestMajor)
+            version="latest"
+            channel="STS" # TODO this doesn't do what it should https://github.com/dotnet/install-scripts/issues/418
+            ;;
+
+        latestMinor)
+            version="latest"
+            channel="${major}.x" # TODO this doesn't work
+            ;;
+
+        latestFeature)
+            version="latest"
+            channel="${major}.${minor}"
+            ;;
+
+        latestPatch)
+            version="latest"
+            channel="${major}.${minor}.${featureBand}xx"
+            ;;
+
+        *)
+            say_err "Unsupported rollForward option: $globaljson_roll_forward"
+            return 1
+            ;;
+        esac
+    fi
+}
+
 # args:
 # azure_feed - $1
 # channel - $2
 # normalized_architecture - $3
 # version - $4
-# json_file - $5
 get_specific_version_from_version() {
     eval $invocation
 
@@ -689,23 +742,15 @@ get_specific_version_from_version() {
     local channel="$2"
     local normalized_architecture="$3"
     local version="$(to_lowercase "$4")"
-    local json_file="$5"
-
-    if [ -z "$json_file" ]; then
-        if [[ "$version" == "latest" ]]; then
-            local version_info
-            version_info="$(get_version_from_latestversion_file "$azure_feed" "$channel" "$normalized_architecture" false)" || return 1
-            say_verbose "get_specific_version_from_version: version_info=$version_info"
-            echo "$version_info" | get_version_from_latestversion_file_content
-            return 0
-        else
-            echo "$version"
-            return 0
-        fi
-    else
+
+    if [[ "$version" == "latest" ]]; then
         local version_info
-        version_info="$(parse_globaljson_file_for_version "$json_file")" || return 1
-        echo "$version_info"
+        version_info="$(get_version_from_latestversion_file "$azure_feed" "$channel" "$normalized_architecture" false)" || return 1
+        say_verbose "get_specific_version_from_version: version_info=$version_info"
+        echo "$version_info" | get_version_from_latestversion_file_content
+        return 0
+    else
+        echo "$version"
         return 0
     fi
 }
@@ -1361,8 +1406,8 @@ generate_akams_links() {
         return 1
     fi
 
-    if [[ -n "$json_file" || "$normalized_version" != "latest" ]]; then
-        # aka.ms links are not needed when exact version is specified via command or json file
+    if [[ "$normalized_version" != "latest" ]]; then
+        # aka.ms links are not needed when exact version is specified via command
         return
     fi
 
@@ -1416,7 +1461,7 @@ generate_regular_links() {
     local feed="$1"
     local valid_legacy_download_link=true
 
-    specific_version=$(get_specific_version_from_version "$feed" "$channel" "$normalized_architecture" "$version" "$json_file") || specific_version='0'
+    specific_version=$(get_specific_version_from_version "$feed" "$channel" "$normalized_architecture" "$version") || specific_version='0'
 
     if [[ "$specific_version" == '0' ]]; then
         say_verbose "Failed to resolve the specific version number using feed '$feed'"
@@ -1503,6 +1548,7 @@ calculate_vars() {
     say_verbose "Normalized product: '$normalized_product'."
     install_root="$(resolve_installation_path "$install_dir")"
     say_verbose "InstallRoot: '$install_root'."
+    say_verbose "Version: '$version'."
 
     normalized_architecture="$(get_normalized_architecture_for_specific_sdk_version "$version" "$normalized_channel" "$normalized_architecture")"
 
@@ -1799,7 +1845,6 @@ do
             echo "      -SkipNonVersionedFiles"
             echo "  --no-cdn,-NoCdn                    Disable downloading from the Azure CDN, and use the uncached feed directly."
             echo "  --jsonfile <JSONFILE>              Determines the SDK version from a user specified global.json file."
-            echo "                                     Note: global.json must have a value for 'SDK:Version'"
             echo "  --keep-zip,-KeepZip                If set, downloaded file is kept."
             echo "  --zip-path, -ZipPath               If set, downloaded file is stored at the specified path."
             echo "  -?,--?,-h,--help,-Help             Shows this help message"
@@ -1836,6 +1881,7 @@ if [ "$internal" = true ] && [ -z "$(echo $feed_credential)" ]; then
 fi
 
 check_min_reqs
+process_globaljson_file
 calculate_vars
 # generate_regular_links call below will 'exit' if the determined version is already installed.
 generate_download_links
diff --git a/tests/Install-Scripts.Test/Assets/GlobalJson/Disable.json b/tests/Install-Scripts.Test/Assets/GlobalJson/Disable.json
new file mode 100644
index 0000000000..2244195a20
--- /dev/null
+++ b/tests/Install-Scripts.Test/Assets/GlobalJson/Disable.json
@@ -0,0 +1,6 @@
+{
+  "sdk": {
+    "version": "8.0.100",
+    "rollForward": "disable"
+  }
+}
diff --git a/tests/Install-Scripts.Test/Assets/GlobalJson/LatestFeature.json b/tests/Install-Scripts.Test/Assets/GlobalJson/LatestFeature.json
new file mode 100644
index 0000000000..391ba3c2a3
--- /dev/null
+++ b/tests/Install-Scripts.Test/Assets/GlobalJson/LatestFeature.json
@@ -0,0 +1,6 @@
+{
+  "sdk": {
+    "version": "8.0.100",
+    "rollForward": "latestFeature"
+  }
+}
diff --git a/tests/Install-Scripts.Test/Assets/GlobalJson/LatestMajor.json b/tests/Install-Scripts.Test/Assets/GlobalJson/LatestMajor.json
new file mode 100644
index 0000000000..d07970ac26
--- /dev/null
+++ b/tests/Install-Scripts.Test/Assets/GlobalJson/LatestMajor.json
@@ -0,0 +1,6 @@
+{
+  "sdk": {
+    "version": "8.0.100",
+    "rollForward": "latestMajor"
+  }
+}
diff --git a/tests/Install-Scripts.Test/Assets/GlobalJson/LatestMinor.json b/tests/Install-Scripts.Test/Assets/GlobalJson/LatestMinor.json
new file mode 100644
index 0000000000..c19a2e057c
--- /dev/null
+++ b/tests/Install-Scripts.Test/Assets/GlobalJson/LatestMinor.json
@@ -0,0 +1,6 @@
+{
+  "sdk": {
+    "version": "8.0.100",
+    "rollForward": "latestMinor"
+  }
+}
diff --git a/tests/Install-Scripts.Test/Assets/GlobalJson/LatestPatch.json b/tests/Install-Scripts.Test/Assets/GlobalJson/LatestPatch.json
new file mode 100644
index 0000000000..b75deba61a
--- /dev/null
+++ b/tests/Install-Scripts.Test/Assets/GlobalJson/LatestPatch.json
@@ -0,0 +1,6 @@
+{
+  "sdk": {
+    "version": "8.0.100",
+    "rollForward": "latestPatch"
+  }
+}
diff --git a/tests/Install-Scripts.Test/Assets/GlobalJson/NoFields.json b/tests/Install-Scripts.Test/Assets/GlobalJson/NoFields.json
new file mode 100644
index 0000000000..9823519c74
--- /dev/null
+++ b/tests/Install-Scripts.Test/Assets/GlobalJson/NoFields.json
@@ -0,0 +1,4 @@
+{
+  "sdk": {
+  }
+}
diff --git a/tests/Install-Scripts.Test/Assets/GlobalJson/VersionOnly.json b/tests/Install-Scripts.Test/Assets/GlobalJson/VersionOnly.json
new file mode 100644
index 0000000000..5ce8495514
--- /dev/null
+++ b/tests/Install-Scripts.Test/Assets/GlobalJson/VersionOnly.json
@@ -0,0 +1,5 @@
+{
+  "sdk": {
+    "version": "8.0.100"
+  }
+}
diff --git a/tests/Install-Scripts.Test/GivenThatIWantToGetTheSdkLinksFromAScript.cs b/tests/Install-Scripts.Test/GivenThatIWantToGetTheSdkLinksFromAScript.cs
index 1997e59685..3732686aac 100644
--- a/tests/Install-Scripts.Test/GivenThatIWantToGetTheSdkLinksFromAScript.cs
+++ b/tests/Install-Scripts.Test/GivenThatIWantToGetTheSdkLinksFromAScript.cs
@@ -44,6 +44,32 @@ public void WhenJsonFileIsPassedToInstallScripts(string filename)
             commandResult.Should().HaveStdOutMatching(@"URL\s#0\s-\s(legacy|primary|aka\.ms):\shttps://");
         }
 
+        [Theory]
+        [InlineData("NoFields.json", "STS")]
+        [InlineData("VersionOnly.json", "8.0.1xx")]
+        [InlineData("LatestPatch.json", "8.0.1xx")]
+        [InlineData("LatestFeature.json", "8.0")]
+        [InlineData("LatestMinor.json", "8.x")]
+        [InlineData("LatestMajor.json", "STS")]
+        [InlineData("Disable.json", "LTS", "8.0.100")]
+        public void WhenGlobalJsonFileIsPassedToInstallScripts(string filename, string channel, string version = "latest")
+        {
+            var installationScriptTestsJsonFile = Path.Combine(Environment.CurrentDirectory, "Assets", "GlobalJson", filename);
+
+            var args = new List<string> { "-verbose", "-dryrun", "-jsonfile", installationScriptTestsJsonFile };
+
+            var commandResult = CreateInstallCommand(args)
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute();
+
+            commandResult.Should().Pass();
+            commandResult.Should().NotHaveStdOutContaining("dryrun");
+            commandResult.Should().NotHaveStdOutContaining("jsonfile");
+            commandResult.Should().HaveStdOutContaining($"Normalized channel: '{channel}'.");
+            commandResult.Should().HaveStdOutContaining($"Version: '{version}'.");
+        }
+
         [Theory]
         [InlineData("-nopath", "")]
         [InlineData("-verbose", "")]
diff --git a/tests/Install-Scripts.Test/Install-Scripts.Test.csproj b/tests/Install-Scripts.Test/Install-Scripts.Test.csproj
index 6e12f37241..7154f68d28 100644
--- a/tests/Install-Scripts.Test/Install-Scripts.Test.csproj
+++ b/tests/Install-Scripts.Test/Install-Scripts.Test.csproj
@@ -27,6 +27,9 @@
     <Content Include="Assets\InstallationScriptTestsWithVersionFieldInTheMiddle.json">
       <CopyToOutputDirectory>Always</CopyToOutputDirectory>
     </Content>
+    <Content Include="Assets\GlobalJson\*.json">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
   </ItemGroup>
 
   <ItemGroup>