This document describes the tools and workflows for applying local policies to a ChromeOS test device using fake_dmserver.
There are two primary ways to use these tools:
orchestrator.py script.blob_generator.py script directly.Before you begin, you must disable root filesystem verification on your test device. This allows you to write the necessary configuration files.
Run these commands in a terminal on your DUT:
sudo /usr/share/vboot/bin/make_dev_ssd.sh --remove_rootfs_verification \ --force && sudo reboot
After the device reboots, remount the filesystem as read-write for your current session:
sudo mount -o remount,rw /
This workflow uses the orchestrator.py script to automate the entire process of generating policies, starting the server, configuring Chrome, and restarting the UI.
Create a simple JSON file with the policies you want to test. A good location is /usr/local/tmp/policies.json.
(See the section “How to Set Valid Policies” below for details on the file format.)
Execute the orchestrator script with the path to your policy file. It will handle everything else.
Run this command on the DUT:
python3 /usr/local/share/policy-test-tool/orchestrator.py \ /usr/local/tmp/policies.json
The script will start the server and restart the UI. You can then log in and verify your policies at chrome://policy. The script will continue to watch your policies.json file for changes.
This workflow is for users who want more control over the process. You will use the generator script to create the policy blob and then manually perform the necessary system actions.
Create a simple JSON file with the policies you want to test (e.g., /usr/local/tmp/policies.json).
Use the blob_generator.py script to convert your simple JSON into the format fake_dmserver understands.
Run this command on the DUT:
# Make sure the output directory exists mkdir -p /var/tmp/dmserver_data python3 /usr/local/share/policy-test-tool/blob_generator.py \ --input-policies=/usr/local/tmp/policies.json \ --output-blob=/var/tmp/dmserver_data/policy.json \ --manual-map=/usr/local/share/policy-test-tool/manual_device_policy_proto_map.yaml
fake_dmserverManually start the fake_dmserver on the device, pointing it to your generated policy blob. This command typically requires root permissions.
Run this command on the DUT:
# Start the server and take note of the host and port it prints out /usr/local/libexec/chrome-binary-tests/fake_dmserver \ --policy-blob-path=/var/tmp/dmserver_data/policy.json
The server will output its address, for example: {"host": "127.0.0.1", "port": 8080}.
Write a configuration file to tell Chrome to use your local server.
Run this command on the DUT (you will need to be root):
# Replace the URL with the host and port from the previous step echo "--device-management-url=http://127.0.0.1:8080/device_management" \ >> /etc/chrome_dev.conf``` ### Step 5: Restart the UI Restart the Chrome UI to force it to reload the configuration and fetch the new policies. **Run this command on the DUT (you will need to be root):** ```bash restart ui
policies.json)The policies.json file has a simple structure. The top-level keys are used to configure the fake_dmserver itself, while the user and device sections define the actual policies to be applied.
You can create this file manually, or you can generate it by following the steps in the converter script described in the next section.
user: (Object) A dictionary where keys are user policy names and values are the policy values.device: (Object) A dictionary where keys are device policy names and values are the policy values.policy_user: (String) The user ID (e.g., email address) to embed in policy responses. This must match the user on the ChromeOS device, or the policy will be rejected.managed_users: (Array of Strings) A list of users that the server should treat as managed. Use "*" to treat all users as managed.device_affiliation_ids: (Array of Strings) A list of affiliation IDs for the device.user_affiliation_ids: (Array of Strings) A list of affiliation IDs for the user.directory_api_id: (String) The Directory API ID of the device.robot_api_auth_code: (String) The authorization code for the robot account, used during enterprise enrollment.allow_set_device_attributes: (Boolean) If true, allows device attributes to be set by the client. Defaults to true.use_universal_signing_keys: (Boolean) If true, the server will use a universal test key to sign policy blobs. This is recommended for most local testing scenarios.current_key_index: (Integer) The index of the signing key to use.request_errors: (Object) A dictionary to simulate server errors. The keys are request types (e.g., "register", "policy") and the values are HTTP error codes (e.g., 500).initial_enrollment_state: (Object) A dictionary defining the initial state for zero-touch enrollment.policies.json{ "policy_user": "<user>@gmail.com", "managed_users": ["*"], "user": { "AllowDinosaurEasterEgg": true, "ShowHomeButton": true, "HomepageLocation": "https://www.google.com", "RestoreOnStartup": 1, "URLBlocklist": [ "example.com", "unwanted-site.org" ], "ManagedBookmarks": "[{\"name\":\"Google Search\",\"url\":\"https://www.google.com\"},{\"name\":\"Internal Tools\",\"children\":[{\"name\":\"Moma\",\"url\":\"https://moma.corp.google.com\"},{\"name\":\"Critique\",\"url\":\"https://critique.corp.google.com\"}]},{\"name\":\"News\",\"url\":\"https://news.google.com\"}]" }, "device": { "DeviceGuestModeEnabled": true, "DevicePolicyRefreshRate": 3600000, "DeviceLocalAccounts": [ { "account_id": "<user>@gmail.com", "type": "KIOSK_IWA", "isolated_kiosk_app": { "update_manifest_url": "https://github.com/chromeos/iwa-sink/releases/latest/download/update.json", "web_bundle_id": "aiv4bxauvcu3zvbu6r5yynoh4atkzqqaoeof5mwz54b4zfywcrjuoaacai" } } ], "DeviceLocalAccountAutoLoginId": "<user>@gmail.com" }, "use_universal_signing_keys": true }
For full details on available policies and value formats, please refer to the [Chrome Enterprise policies documentation] (https://chromeenterprise.google.com/policies/).
The orchestrator.py script allows you to pass additional command-line flags directly to Chrome using the --chrome-flags argument. This is useful for enabling experimental features, debugging, or overriding default Chrome behavior.
You can specify this argument multiple times for different flags:
python3 /usr/local/share/policy-test-tool/orchestrator.py \ /usr/local/tmp/policies.json \ --chrome-flags="--remote-debugging-port=9222" \ --chrome-flags="--enable-features=IsolatedWebAppDevMode"
chrome://policy dumpIf you want to replicate the policy state of an existing device, you can use the policy_dump_converter.py script.
The JSON file you export from chrome://policy is different from the simplified policies.json format required by these tools. The dump from chrome://policy is a detailed report of the current state, including metadata like policy scope, source, and whether it was successfully applied.
The policies.json format, on the other hand, is a simple, clean definition of the policies you want to set. The converter script bridges this gap by extracting only the necessary information (name, value, and scope) and structuring it correctly for the fake_dmserver.
Step 1: Export Policies from Chrome Navigate to chrome://policy on the device you want to copy policies from, click “Export to JSON”, and save the file.
Step 2: Run the Converter Script Run the converter script on the DUT, providing the exported JSON, a path for the new policies.json file, and the managed user's email.
python3 /usr/local/share/policy-test-tool/policy_dump_converter.py \ --input-dump=/path/to/your/policy_export.json \ --output-policies=/usr/local/tmp/policies.json \ --policy-user=user@managedchrome.com
Step 3: Use the Generated File The newly created /usr/local/tmp/policies.json can now be used with the orchestrator.py script as described in the “Automated Usage” section.
If you see a warning like WARNING: Policy 'YourPolicyName' not found in protobuf schema. Skipping., it means the version of the policy-test-tool on your device is older than the version of Chrome running on it.
To fix this:
policy-test-tool package to your DUT.This will update the policy definitions used by the tool to match the latest version of Chrome.
If you are developing a new policy that has not yet been landed in the Chromium source tree, the testing tools will not recognize it. To test it, you need to manually update the policy definitions in your local checkout before building the package.
policy_templates.jsonThis file is the source of truth for all Chrome policies. You need to add a definition for your new policy here.
Edit the file: chromium/src/components/policy/resources/policy_templates.json
Add your policy definition to the policy_definitions list. Ensure the name, type, schema, and supported_on fields are correct.
{ 'name': 'MyNewAwesomePolicy', 'type': 'boolean', 'schema': { 'type': 'boolean' }, 'supported_on': ['chrome_os:145-'], 'features': { 'dynamic_refresh': true, 'per_profile': true }, 'example_value': true, 'caption': 'Enable My New Awesome Policy', 'desc': 'When this policy is enabled, it activates my new awesome feature.' }
After updating the policy definitions, you must regenerate the chrome_settings.proto file, which is used to build the Python modules.
chromium/src directory:python3 components/policy/tools/generate_policy_source.py \ --chrome-settings-protobuf=components/policy/proto/chrome_settings.proto \ --policy-templates-file=components/policy/resources/policy_templates.json \ --target-platform="chrome_os"
Now that your local source code includes the new policy definition, you can rebuild and deploy the policy-test-tool.
.proto file.emerge-<board> chromeos-base/policy-test-tool
cros deploy <DUT_IP> chromeos-base/policy-test-tool
You can now add your new policy to your policies.json file and use the orchestrator script.
/usr/local/tmp/policies.json on the DUT and add your new policy to the user or device section.python3 /usr/local/share/policy-test-tool/orchestrator.py \ /usr/local/tmp/policies.json
The tool will now recognize and apply your new policy.
If you are making changes to the policy-test-tool scripts themselves (e.g., orchestrator.py, blob_generator.py, policy_dump_converter.py), follow these steps to test your changes on the DUT:
cros_workonOn your development machine, enable cros_workon for the policy-test-tool package. This allows you to make changes to the source code and have them reflected in the build.
cros_workon --board=<board> start chromeos-base/policy-test-tool
Edit the Python scripts in your local ChromiumOS checkout (chromiumos/src/chromium/src/components/policy/tools/fake_dmserver/orchestrator.py, chromiumos/src/chromium/src/components/policy/tools/fake_dmserver/blob_generator.py, chromiumos/src/chromium/src/components/policy/tools/fake_dmserver/policy_dump_converter.py) or directly on the DUT at /usr/local/share/policy-test-tool/.
After making your changes, rebuild and deploy the policy-test-tool package to your DUT. This will install your modified scripts on the device.
emerge-<board> chromeos-base/policy-test-tool
cros deploy <DUT_IP> chromeos-base/policy-test-tool
Once deployed, your changes to the policy-test-tool scripts will be active on the DUT. You can then run the orchestrator or other scripts as usual to test your modifications.
python3 /usr/local/share/policy-test-tool/orchestrator.py \ /usr/local/tmp/policies.json