• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

.github/H24-Jun-2021-245171

azurelinuxagent/H24-Jun-2021-24,92017,867

bin/H03-May-2022-6,2945,245

ci/H24-Jun-2021-148118

config/H24-Jun-2021-1,855418

init/H03-May-2022-500314

tests/H24-Jun-2021-30,14922,736

.gitattributesH A D24-Jun-20212.5 KiB6459

.gitignoreH A D24-Jun-20211 KiB9676

CODEOWNERSH A D24-Jun-2021681 2419

ChangelogH A D24-Jun-20211.2 KiB3929

MAINTENANCE.mdH A D24-Jun-2021715 1712

MANIFESTH A D24-Jun-2021376 1817

MANIFEST.inH A D24-Jun-202176 43

NOTICEH A D24-Jun-2021161 64

README.mdH A D24-Jun-202122 KiB667448

__main__.pyH A D24-Jun-2021682 212

makepkg.pyH A D24-Jun-20213.9 KiB11090

setup.pyH A D03-May-202210 KiB274201

README.md

1# Microsoft Azure Linux Agent
2
3## Develop branch status
4
5[![Travis CI](https://travis-ci.org/Azure/WALinuxAgent.svg?branch=develop)](https://travis-ci.org/Azure/WALinuxAgent/branches)
6[![CodeCov](https://codecov.io/gh/Azure/WALinuxAgent/branch/develop/graph/badge.svg)](https://codecov.io/gh/Azure/WALinuxAgent/branch/develop)
7
8Each badge below represents our basic validation tests for an image, which are executed several times each day. These include provisioning, user account, disk, extension and networking scenarios.
9
10Note: These badges represent testing to our develop branch which might not be stable. For a stable build please use master branch instead.
11
12Image | Status |
13------|--------|
14Canonical UbuntuServer 14.04.5-LTS|![badge](https://dcrbadges.blob.core.windows.net/scenarios/Canonical_UbuntuServer_14.04.5-LTS__agent--bvt.svg)
15Canonical UbuntuServer 14.04.5-DAILY-LTS|![badge](https://dcrbadges.blob.core.windows.net/scenarios/Canonical_UbuntuServer_14.04.5-DAILY-LTS__agent--bvt.svg)
16Canonical UbuntuServer 16.04-LTS|![badge](https://dcrbadges.blob.core.windows.net/scenarios/Canonical_UbuntuServer_16.04-LTS__agent--bvt.svg)
17Canonical UbuntuServer 16.04-DAILY-LTS|![badge](https://dcrbadges.blob.core.windows.net/scenarios/Canonical_UbuntuServer_16.04-DAILY-LTS__agent--bvt.svg)
18Canonical UbuntuServer 18.04-LTS|![badge](https://dcrbadges.blob.core.windows.net/scenarios/Canonical_UbuntuServer_18.04-LTS__agent--bvt.svg)
19Canonical UbuntuServer 18.04-DAILY-LTS|![badge](https://dcrbadges.blob.core.windows.net/scenarios/Canonical_UbuntuServer_18.04-DAILY-LTS__agent--bvt.svg)
20Credativ Debian 8|![badge](https://dcrbadges.blob.core.windows.net/scenarios/Credativ_Debian_8__agent--bvt.svg)
21Credativ Debian 9|![badge](https://dcrbadges.blob.core.windows.net/scenarios/Credativ_Debian_9__agent--bvt.svg)
22OpenLogic CentOS 6.9|![badge](https://dcrbadges.blob.core.windows.net/scenarios/OpenLogic_CentOS_6.9__agent--bvt.svg)
23OpenLogic CentOS 7.4|![badge](https://dcrbadges.blob.core.windows.net/scenarios/OpenLogic_CentOS_7.4__agent--bvt.svg)
24RedHat RHEL 6.9|![badge](https://dcrbadges.blob.core.windows.net/scenarios/RedHat_RHEL_6.9__agent--bvt.svg)
25RedHat RHEL 7-RAW|![badge](https://dcrbadges.blob.core.windows.net/scenarios/RedHat_RHEL_7-RAW__agent--bvt.svg)
26SUSE SLES 12-SP5|![badge](https://dcrbadges.blob.core.windows.net/scenarios/SUSE_SLES_12-SP5__agent--bvt.svg)
27
28## Introduction
29
30The Microsoft Azure Linux Agent (waagent) manages Linux provisioning and VM interaction with the Azure Fabric Controller. It provides the following
31functionality for Linux IaaS deployments:
32
33* Image Provisioning
34  * Creation of a user account
35  * Configuring SSH authentication types
36  * Deployment of SSH public keys and key pairs
37  * Setting the host name
38  * Publishing the host name to the platform DNS
39  * Reporting SSH host key fingerprint to the platform
40  * Resource Disk Management
41  * Formatting and mounting the resource disk
42  * Configuring swap space
43
44* Networking
45  * Manages routes to improve compatibility with platform DHCP servers
46  * Ensures the stability of the network interface name
47
48* Kernel
49  * Configure virtual NUMA (disable for kernel <2.6.37)
50  * Configure SCSI timeouts for the root device (which could be remote)
51
52* Diagnostics
53  * Console redirection to the serial port
54
55* SCVMM Deployments
56  * Detect and bootstrap the VMM agent for Linux when running in a System
57    Center Virtual Machine Manager 2012R2 environment
58
59* VM Extension
60  * Inject component authored by Microsoft and Partners into Linux VM (IaaS)
61    to enable software and configuration automation
62  * VM Extension reference implementation on [GitHub](https://github.com/Azure/azure-linux-extensions)
63
64## Communication
65
66The information flow from the platform to the agent occurs via two channels:
67
68* A boot-time attached DVD for IaaS deployments.
69  This DVD includes an OVF-compliant configuration file that includes all
70  provisioning information other than the actual SSH keypairs.
71
72* A TCP endpoint exposing a REST API used to obtain deployment and topology
73  configuration.
74
75The agent will use an HTTP proxy if provided via the `http_proxy` (for `http` requests) or
76`https_proxy` (for `https` requests) environment variables. The `HttpProxy.Host` and
77`HttpProxy.Port` configuration variables (see below), if used, will override the environment
78settings. Due to limitations of Python, the agent *does not* support HTTP proxies requiring
79authentication.
80
81## Requirements
82
83The following systems have been tested and are known to work with the Azure
84Linux Agent.  Please note that this list may differ from the official list
85of supported systems on the Microsoft Azure Platform as described [here](https://docs.microsoft.com/en-us/azure/virtual-machines/linux/endorsed-distros).
86
87Waagent depends on some system packages in order to function properly:
88
89* Python 2.6+
90* OpenSSL 1.0+
91* OpenSSH 5.3+
92* Filesystem utilities: sfdisk, fdisk, mkfs, parted
93* Password tools: chpasswd, sudo
94* Text processing tools: sed, grep
95* Network tools: ip-route
96
97## Installation
98
99Installation via your distribution's package repository is preferred.
100You can also customize your own RPM or DEB packages using the configuration
101samples provided (see deb and rpm sections below).
102
103For more advanced installation options, such as installing to custom locations or prefixes, you can use **setuptools** to install from source by running:
104
105```bash
106    sudo python setup.py install --register-service
107```
108
109For Python 3, use:
110
111```bash
112    sudo python3 setup.py install --register-service
113```
114
115You can view more installation options by running:
116
117```bash
118    sudo python setup.py install --help
119```
120
121The agent's log file is kept at `/var/log/waagent.log`.
122
123## Upgrade
124
125Upgrading via your distribution's package repository is strongly preferred.
126
127If upgrading manually, same with installation above by running:
128
129```bash
130    sudo python setup.py install --force
131```
132
133Restart waagent service,for most of linux distributions:
134
135```bash
136    sudo service waagent restart
137```
138
139For Ubuntu, use:
140
141```bash
142    sudo service walinuxagent restart
143```
144
145For CoreOS, use:
146
147```bash
148    sudo systemctl restart waagent
149```
150
151## Command line options
152
153### Flags
154
155`-verbose`: Increase verbosity of specified command
156
157`-force`: Skip interactive confirmation for some commands
158
159### Commands
160
161`-help`: Lists the supported commands and flags.
162
163`-deprovision`: Attempt to clean the system and make it suitable for re-provisioning, by deleting the following:
164
165* All SSH host keys (if Provisioning.RegenerateSshHostKeyPair is 'y' in the configuration file)
166* Nameserver configuration in /etc/resolv.conf
167* Root password from /etc/shadow (if Provisioning.DeleteRootPassword is 'y' in the configuration file)
168* Cached DHCP client leases
169* Resets host name to localhost.localdomain
170
171  **WARNING!** Deprovision does not guarantee that the image is cleared of all sensitive information and suitable for redistribution.
172
173`-deprovision+user`: Performs everything under deprovision (above) and also deletes the last provisioned user account and associated data.
174
175`-version`: Displays the version of waagent
176
177`-serialconsole`: Configures GRUB to mark ttyS0 (the first serial port) as the boot console. This ensures that kernel bootup logs are sent to the serial port and made available for debugging.
178
179`-daemon`: Run waagent as a daemon to manage interaction with the platform. This argument is specified to waagent in the waagent init script.
180
181`-start`: Run waagent as a background process
182
183`-collect-logs [-full]`: Runs the log collector utility that collects relevant agent logs for debugging and stores them in the agent folder on disk. Exact location will be shown when run. Use flag `-full` for more exhaustive log collection.
184
185## Configuration
186
187A configuration file (/etc/waagent.conf) controls the actions of waagent. Blank lines and lines whose first character is a `#` are ignored (end-of-line comments are *not* supported).
188
189A sample configuration file is shown below:
190
191```yml
192Extensions.Enabled=y
193Extensions.GoalStatePeriod=6
194Extensions.GoalStateHistoryCleanupPeriod=86400
195Provisioning.Agent=auto
196Provisioning.DeleteRootPassword=n
197Provisioning.RegenerateSshHostKeyPair=y
198Provisioning.SshHostKeyPairType=rsa
199Provisioning.MonitorHostName=y
200Provisioning.DecodeCustomData=n
201Provisioning.ExecuteCustomData=n
202Provisioning.PasswordCryptId=6
203Provisioning.PasswordCryptSaltLength=10
204ResourceDisk.Format=y
205ResourceDisk.Filesystem=ext4
206ResourceDisk.MountPoint=/mnt/resource
207ResourceDisk.MountOptions=None
208ResourceDisk.EnableSwap=n
209ResourceDisk.EnableSwapEncryption=n
210ResourceDisk.SwapSizeMB=0
211Logs.Verbose=n
212Logs.Collect=n
213Logs.CollectPeriod=3600
214OS.AllowHTTP=n
215OS.RootDeviceScsiTimeout=300
216OS.EnableFIPS=n
217OS.OpensslPath=None
218OS.SshClientAliveInterval=180
219OS.SshDir=/etc/ssh
220HttpProxy.Host=None
221HttpProxy.Port=None
222CGroups.EnforceLimits=y
223CGroups.Excluded=customscript,runcommand
224```
225
226The various configuration options are described in detail below. Configuration
227options are of three types : Boolean, String or Integer. The Boolean
228configuration options can be specified as "y" or "n". The special keyword "None"
229may be used for some string type configuration entries as detailed below.
230
231### Configuration File Options
232
233#### __Extensions.Enabled__
234
235_Type: Boolean_
236_Default: y_
237
238This allows the user to enable or disable the extension handling functionality in the
239agent. Valid values are "y" or "n". If extension handling is disabled, the goal state
240will still be processed and VM status is still reported, but only every 5 minutes.
241Extension config within the goal state will be ignored. Note that functionality such
242as password reset, ssh key updates and backups depend on extensions. Only disable this
243if you do not need extensions at all.
244
245_Note_: disabling extensions in this manner is not the same as running completely
246without the agent. In order to do that, the `provisionVMAgent` flag must be set at
247provisioning time, via whichever API is being used. We will provide more details on
248this on our wiki when it is generally available.
249
250#### __Extensions.GoalStatePeriod__
251
252_Type: Integer_
253_Default: 6_
254
255How often to poll for new goal states (in seconds) and report the status of the VM
256and extensions. Goal states describe the desired state of the extensions on the VM.
257
258_Note_: setting up this parameter to more than a few minutes can make the state of
259the VM be reported as unresponsive/unavailable on the Azure portal. Also, this
260setting affects how fast the agent starts executing extensions.
261
262#### __Extensions.GoalStateHistoryCleanupPeriod__
263
264_Type: Integer_
265_Default: 86400 (24 hours)_
266
267How often to clean up the history folder of the agent. The agent keeps past goal
268states on this folder, each goal state represented with a set of small files. The
269history is useful to debug issues in the agent or extensions.
270
271#### __AutoUpdate.Enabled__
272
273_Type: Boolean_
274_Default: y_
275
276Enables auto-update of the Extension Handler. The Extension Handler is responsible
277for managing extensions and reporting VM status. The core functionality of the agent
278is contained in the Extension Handler, and we encourage users to enable this option
279in order to maintain an up to date version.
280
281On most distros the default value is 'y'.
282
283For more information on the agent version, see our [FAQ](https://github.com/Azure/WALinuxAgent/wiki/FAQ#what-does-goal-state-agent-mean-in-waagent---version-output).
284
285#### __Provisioning.Agent__
286
287_Type: String_
288_Default: auto_
289
290Choose which provisioning agent to use (or allow waagent to figure it out by
291specifying "auto"). Possible options are "auto" (default), "waagent", "cloud-init",
292or "disabled".
293
294#### __Provisioning.Enabled__ (*removed in 2.2.45*)
295
296_Type: Boolean_
297_Default: y_
298
299This allows the user to enable or disable the provisioning functionality in the
300agent. Valid values are "y" or "n". If provisioning is disabled, SSH host and
301user keys in the image are preserved and any configuration specified in the
302Azure provisioning API is ignored.
303
304_Note_: This configuration option has been removed and has no effect. waagent
305now auto-detects cloud-init as a provisioning agent (with an option to override
306with `Provisioning.Agent`).
307
308#### __Provisioning.MonitorHostName__
309
310_Type: Boolean_
311_Default: n_
312
313Monitor host name changes and publish changes via DHCP requests.
314
315#### __Provisioning.MonitorHostNamePeriod__
316
317_Type: Integer_
318_Default: 30_
319
320How often to monitor host name changes (in seconds). This setting is ignored if
321MonitorHostName is not set.
322
323#### __Provisioning.UseCloudInit__
324
325_Type: Boolean_
326_Default: n_
327
328This options enables / disables support for provisioning by means of cloud-init.
329When true ("y"), the agent will wait for cloud-init to complete before installing
330extensions and processing the latest goal state. _Provisioning.Enabled_ must be
331disabled ("n") for this option to have an effect. Setting _Provisioning.Enabled_ to
332true ("y") overrides this option and runs the built-in agent provisioning code.
333
334_Note_: This configuration option has been removed and has no effect. waagent
335now auto-detects cloud-init as a provisioning agent (with an option to override
336with `Provisioning.Agent`).
337
338#### __Provisioning.DeleteRootPassword__
339
340_Type: Boolean_
341_Default: n_
342
343If set, the root password in the /etc/shadow file is erased during the
344provisioning process.
345
346#### __Provisioning.RegenerateSshHostKeyPair__
347
348_Type: Boolean_
349_Default: y_
350
351If set, all SSH host key pairs (ecdsa, dsa and rsa) are deleted during the
352provisioning process from /etc/ssh/. And a single fresh key pair is generated.
353The encryption type for the fresh key pair is configurable by the
354Provisioning.SshHostKeyPairType entry. Please note that some distributions will
355re-create SSH key pairs for any missing encryption types when the SSH daemon is
356restarted (for example, upon a reboot).
357
358#### __Provisioning.SshHostKeyPairType__
359
360_Type: String_
361_Default: rsa_
362
363This can be set to an encryption algorithm type that is supported by the SSH
364daemon on the VM. The typically supported values are "rsa", "dsa" and "ecdsa".
365Note that "putty.exe" on Windows does not support "ecdsa". So, if you intend to
366use putty.exe on Windows to connect to a Linux deployment, please use "rsa" or
367"dsa".
368
369#### __Provisioning.MonitorHostName__
370
371_Type: Boolean_
372_Default: y_
373
374If set, waagent will monitor the Linux VM for hostname changes (as returned by
375the "hostname" command) and automatically update the networking configuration in
376the image to reflect the change. In order to push the name change to the DNS
377servers, networking will be restarted in the VM. This will result in brief loss
378of Internet connectivity.
379
380#### __Provisioning.DecodeCustomData__
381
382_Type: Boolean_
383_Default: n_
384
385If set, waagent will decode CustomData from Base64.
386
387#### __Provisioning.ExecuteCustomData__
388
389_Type: Boolean_
390_Default: n_
391
392If set, waagent will execute CustomData after provisioning.
393
394#### __Provisioning.PasswordCryptId__
395
396_Type: String_
397_Default: 6_
398
399Algorithm used by crypt when generating password hash.
400
401* 1 - MD5
402* 2a - Blowfish
403* 5 - SHA-256
404* 6 - SHA-512
405
406#### __Provisioning.PasswordCryptSaltLength__
407
408_Type: String_
409_Default: 10_
410
411Length of random salt used when generating password hash.
412
413#### __ResourceDisk.Format__
414
415_Type: Boolean_
416_Default: y_
417
418If set, the resource disk provided by the platform will be formatted and mounted by waagent if the filesystem type requested by the user in "ResourceDisk.Filesystem" is anything other than "ntfs". A single partition of
419type Linux (83) will be made available on the disk. Note that this partition will not be formatted if it can be successfully mounted.
420
421#### __ResourceDisk.Filesystem__
422
423_Type: String_
424_Default: ext4_
425
426This specifies the filesystem type for the resource disk. Supported values vary
427by Linux distribution. If the string is X, then mkfs.X should be present on the
428Linux image. SLES 11 images should typically use 'ext3'. BSD images should use
429'ufs2' here.
430
431#### __ResourceDisk.MountPoint__
432
433_Type: String_
434_Default: /mnt/resource_
435
436This specifies the path at which the resource disk is mounted.
437
438#### __ResourceDisk.MountOptions__
439
440_Type: String_
441_Default: None_
442
443Specifies disk mount options to be passed to the mount -o command. This is a comma
444separated list of values, ex. 'nodev,nosuid'. See mount(8) for details.
445
446#### __ResourceDisk.EnableSwap__
447
448_Type: Boolean_
449_Default: n_
450
451If set, a swap file (/swapfile) is created on the resource disk and added to the
452system swap space.
453
454#### __ResourceDisk.EnableSwapEncryption__
455
456_Type: Boolean_
457_Default: n_
458
459If set, the swap file (/swapfile) is mounted as an encrypted filesystem (flag supported only on FreeBSD.)
460
461#### __ResourceDisk.SwapSizeMB__
462
463_Type: Integer_
464_Default: 0_
465
466The size of the swap file in megabytes.
467
468#### __Logs.Verbose__
469
470_Type: Boolean_
471_Default: n_
472
473If set, log verbosity is boosted. Waagent logs to /var/log/waagent.log and
474leverages the system logrotate functionality to rotate logs.
475
476
477#### __Logs.Collect__
478
479_Type: Boolean_
480_Default: n_
481
482If set, agent logs will be periodically collected and uploaded to a secure location for improved supportability.
483
484#### __Logs.CollectPeriod__
485
486_Type: Integer_
487_Default: 3600_
488
489This configures how frequently to collect and upload logs. Default is each hour.
490
491NOTE: This only takes effect if the Logs.Collect option is enabled.
492
493#### __OS.AllowHTTP__
494
495_Type: Boolean_
496_Default: n_
497
498If SSL support is not compiled into Python, the agent will fail all HTTPS requests.
499You can set this option to 'y' to make the agent fall-back to HTTP, instead of failing the requests.
500
501NOTE: Allowing HTTP may unintentionally expose secure data.
502
503#### __OS.EnableRDMA__
504
505_Type: Boolean_
506_Default: n_
507
508If set, the agent will attempt to install and then load an RDMA kernel driver
509that matches the version of the firmware on the underlying hardware.
510
511#### __OS.EnableFIPS__
512
513_Type: Boolean_
514_Default: n_
515
516If set, the agent will emit into the environment "OPENSSL_FIPS=1" when executing
517OpenSSL commands. This signals OpenSSL to use any installed FIPS-compliant libraries.
518Note that the agent itself has no FIPS-specific code. _If no FIPS-compliant certificates are
519installed, then enabling this option will cause all OpenSSL commands to fail._
520
521#### __OS.MonitorDhcpClientRestartPeriod__
522
523_Type: Integer_
524_Default: 30_
525
526The agent monitor restarts of the DHCP client and restores network rules when it happens. This
527setting determines how often (in seconds) to monitor for restarts.
528
529#### __OS.RootDeviceScsiTimeout__
530
531_Type: Integer_
532_Default: 300_
533
534This configures the SCSI timeout in seconds on the root device. If not set, the
535system defaults are used.
536
537#### __OS.RootDeviceScsiTimeoutPeriod__
538
539_Type: Integer_
540_Default: 30_
541
542How often to set the SCSI timeout on the root device (in seconds). This setting is
543ignored if RootDeviceScsiTimeout is not set.
544
545#### __OS.OpensslPath__
546
547_Type: String_
548_Default: None_
549
550This can be used to specify an alternate path for the openssl binary to use for
551cryptographic operations.
552
553#### __OS.RemovePersistentNetRulesPeriod__
554_Type: Integer_
555_Default: 30_
556
557How often to remove the udev rules for persistent network interface names (75-persistent-net-generator.rules
558and /etc/udev/rules.d/70-persistent-net.rules) (in seconds)
559
560#### __OS.SshClientAliveInterval__
561
562_Type: Integer_
563_Default: 180_
564
565This values sets the number of seconds the agent uses for the SSH ClientAliveInterval configuration option.
566
567#### __OS.SshDir__
568
569_Type: String_
570_Default: `/etc/ssh`_
571
572This option can be used to override the normal location of the SSH configuration
573directory.
574
575#### __HttpProxy.Host, HttpProxy.Port__
576
577_Type: String_
578_Default: None_
579
580If set, the agent will use this proxy server to access the internet. These values
581*will* override the `http_proxy` or `https_proxy` environment variables. Lastly,
582`HttpProxy.Host` is required (if to be used) and `HttpProxy.Port` is optional.
583
584#### __CGroups.EnforceLimits__
585
586_Type: Boolean_
587_Default: y_
588
589If set, the agent will attempt to set cgroups limits for cpu and memory for the agent process itself
590as well as extension processes. See the wiki for further details on this.
591
592#### __CGroups.Excluded__
593
594_Type: String_
595_Default: customscript,runcommand_
596
597The list of extensions which will be excluded from cgroups limits. This should be comma separated.
598
599### Telemetry
600
601WALinuxAgent collects usage data and sends it to Microsoft to help improve our products and services. The data collected is used to track service health and
602assist with Azure support requests. Data collected does not include any personally identifiable information. Read our [privacy statement](http://go.microsoft.com/fwlink/?LinkId=521839)
603to learn more.
604
605WALinuxAgent does not support disabling telemetry at this time. WALinuxAgent must be removed to disable telemetry collection. If you need this feature,
606please open an issue in GitHub and explain your requirement.
607
608### Appendix
609
610We do not maintain packaging information in this repo but some samples are shown below as a reference. See the downstream distribution repositories for officially maintained packaging.
611
612#### deb packages
613
614The official Ubuntu WALinuxAgent package can be found [here](https://launchpad.net/ubuntu/+source/walinuxagent).
615
616Run once:
617
6181. Install required packages
619
620   ```bash
621   sudo apt-get -y install ubuntu-dev-tools pbuilder python-all debhelper
622   ```
623
6242. Create the pbuilder environment
625
626   ```bash
627   sudo pbuilder create --debootstrapopts --variant=buildd
628   ```
629
6303. Obtain `waagent.dsc` from a downstream package repo
631
632To compile the package, from the top-most directory:
633
6341. Build the source package
635
636   ```bash
637   dpkg-buildpackage -S
638   ```
639
6402. Build the package
641
642   ```bash
643   sudo pbuilder build waagent.dsc
644   ```
645
6463. Fetch the built package, usually from `/var/cache/pbuilder/result`
647
648#### rpm packages
649
650The instructions below describe how to build an rpm package.
651
6521. Install setuptools
653
654   ```bash
655   curl https://bootstrap.pypa.io/ez_setup.py -o - | python
656   ```
657
6582. The following command will build the binary and source RPMs:
659
660   ```bash
661   python setup.py bdist_rpm
662   ```
663
664-----
665
666This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
667