Почему Apps Script не может достучаться до вашего внутреннего приложения через VPN
Apps Script работает в сети Google, а не на ноутбуке с VPN. Проектируйте путь данных для машины, которая отправляет запрос.
Панель нормально открывается в браузере на ноутбуке, но тот же скрипт таблицы завершается ошибкой недоступного адреса. Включение и выключение VPN ничего не меняет, потому что запрос вообще не покидает ноутбук. Apps Script выполняется в инфраструктуре Google и открывает собственное соединение оттуда. Этот единственный факт определяет весь дизайн.
Разделите два вопроса, прежде чем что-то менять: какая машина отправляет запрос и какие учётные данные принимает получатель.
Запрос никогда не начинается на вашем ноутбуке
Вкладка браузера отправляет с ноутбука, через адаптер VPN, внутренний суффикс DNS и в частную подсеть. UrlFetchApp отправляет с узла, которым управляет Google, поэтому он видит резолверы Google, исходящие адреса Google и ни одного из ваших маршрутов split-tunnel. Два пути разделяют документ таблицы, а не сетевое расположение. В журнале доступа приложения попытка скрипта отображается как адрес Google, а не как адрес из пула VPN, и это самое быстрое подтверждение, которое вы получите.
Поэтому локальные исправления ничего не дают. Направить ноутбук на другой резолвер, перезапустить клиент VPN или добавить запись в hosts — это меняет путь браузера и оставляет путь скрипта точно таким, каким он был.
Что это исключает
До конечной точки, доступной только изнутри, из Apps Script не добраться, а список разрешённых IP-адресов в этом положении — слабая замена контролю доступа. Исходящие диапазоны Google широки, разделяются с другими клиентами и их невозможно привязать к одному скрипту. Справка UrlFetchApp указывает, что запросы происходят из пула IP-адресов Google, и это авторитетная версия такого утверждения. Открытие внутреннего приложения этим диапазонам пропускает трафик, который вы не можете атрибутировать.
Сеансы браузера ведут себя так же. Файл cookie или сеанс SSO в браузере ноутбука не путешествует с запросом, который является новым клиентом HTTPS без состояния сеанса. Всё, что зависит от перенаправления SSO в браузере, заканчивается на этом перенаправлении, а не на ваших данных.
Две работоспособные формы
Первая форма даёт скрипту одобренный API. Опубликуйте внутренний сервис за шлюзом, который проверяет подлинность вызывающей стороны, а затем пусть скрипт предъявит учётные данные, которые шлюз принимает, например токен сервисного аккаунта. В Apps Script это означает библиотеку OAuth2 или ScriptApp.getOAuthToken(), плюс список разрешённых идентичностей вызывающей стороны на стороне API вместо списка IP-адресов.
Вторая форма меняет направление. Внутренняя задача опрашивает частную систему и публикует контролируемый результат в Sheet, объект Cloud Storage или таблицу BigQuery, которую читает скрипт. Задача Cloud Run, локальный планировщик или существующий шаг ETL могут быть этой задачей, и ни одному из них не нужно принимать входящее соединение. Затем таблица потребляет набор данных вместо интерфейса.
Какая форма победит, зависит от свежести, контроля на уровне полей и от того, кто должен иметь возможность проверять читателя.
Что должно содержать опубликованное
Потоку в обратном направлении нужен явный контракт: поля, которые он несёт, интервал обновления, группа читателей и то, что происходит при сбое внутренней задачи. Без этих четырёх таблица тихо показывает вчерашние числа. Добавьте сгенерированный столбец отметки времени и количество строк, чтобы устаревшая или частичная публикация была видна в самой таблице, без того чтобы кто-то открывал журнал.
Направлению API тоже нужен контракт: какие учётные данные принимает шлюз, какие scopes несёт токен и какие идентичности вызывающей стороны разрешены. Ответ 200 со страницей входа — не успешный вызов, поэтому проверяйте тело, а не только код состояния.
Читайте ошибку с правильной стороны
Apps Script сообщает о недоступном узле как об исключении недоступного адреса, и это не сообщение браузера об отказе в соединении. 403 от шлюза означает, что запрос дошёл и учётные данные или политика были отклонены. HTML-страница входа означает, что запрос достиг интерактивного слоя, который скрипт не может завершить. Три разных сбоя с тремя разными ответственными.
Держите журнал скрипта рядом с результатом браузера, а не заменяйте одно другим. Оставшийся вопрос — это обычное решение об интеграции: дать этой среде нужный доступ или доставить ей контролируемый результат.