networktocode / fortimanager-ansible Goto Github PK
View Code? Open in Web Editor NEWAnsible Modules to manage Fortinet FortiManager
License: Other
Ansible Modules to manage Fortinet FortiManager
License: Other
Handle default values outside of module parameters and normalize data: lists should check for strings and convert to list, and data that can be represented as both int and str should be normalized to the FortiManager expected type. Handling outside of module params gives full support for provider.
Used to be able to directly create/add an ip pool with only dynamic mappings with fortimgr_ip_pool_map without specifying ip pool default mapping values like below, but it doesn't work anymore (FMG v6.0.?) and returns a FMG returns -9998 error "start ip should be set".
{
"params": [{
"url": "/pm/config/adom/MYADOM/obj/firewall/ippool",
"session": "xxxxxxxxxxxxx",
"data": {
"dynamic_mapping": [{
"endip": "1.1.1.1",
"_scope": [{
"name": "MYFG",
"vdom": "MYVDOM",
}],
"startip": "1.1.1.1",
"comments": "NAT",
"permit-any-host": "enable",
"type": "overload",
}],
"name": "DFLT-OUT-NAT"},
}],
"method": "add",
}
{ "result": [{
"status": {
"code": -9998,
"message": "firewall/ippool/DFLT-OUT-NAT/ : ippool "DFLT-OUT-NAT": start ip should be set",
},
"url": "/pm/config/adom/MYADOM/obj/firewall/ippool"
} ],
}
Same behavior is also true from the GUI.
Seems that in a recent FMG version (I just noticed on 6.0.7), the behaviour changed and you are now required to switch the "configure default value" to "off" to be able to do so.
This however isn't reflected on the API docs.
Seems that to do the same with the API, you need to set the undocumented parameter "_if_no_default" to 1 to restore the same previous behavior.
{
"method": "add",
"params": [
{
"data": {
"_if_no_default": 1,
"dynamic_mapping": [
{
"_scope": [
{
"name": "MYFG",
"vdom": "MYVDOM"
}
],
"comments": "NAT",
"endip": "1.1.1.1",
"permit-any-host": "enable",
"startip": "1.1.1.1",
"type": "overload"
}
],
"name": "DFLT-OUT-NAT"
},
"url": "/pm/config/adom/MYADOM/obj/firewall/ippool"
}
],
}
@jathanism @dgarros @gkroone @jedelman8 @jerej is there any way to unlock the user who logged in via gui. we have bunch of operation engineers if anyone one making adom on locked mode then ansible is failing to execute. how we can handle this programmatically
I have found the resolution, My user was not enable for rpc calls.
i have recently create a playbook with 2 tasks. the first task is making a preview and the second will install it. the second tasks waiting for a prior session to finish.
Fortimgr_policy automatically saves changes even if parameter "lock" is set to false when a session id is provided.
This seems stem from a few instances of
if module.params["session_id"]:
self.save()
at lines 1677, 1724 and 1731 of fortimgr_policy.py, in the config_move function.
Is this the intended behaviour?
I'm also not sure what the purpose of saving the fmg workspace at those points is? Seems like it might be a API workaround to sync the FMG's state before looking up reference policy id/name. Is this right and if so actually/still required?
The behavior as it is right now messes up externally managed (through fortimgr_lock) workspace state. For example, when batch updating a policy package through a playbook, I want the whole FMG workspace to revert (unlock without save) if there is an error in a individual policy change.
JC
When installing a config/policy with fortimgr_install and fortimanager determines there are no actual changes / install required on the fortigate devices, the module returns a failure to ansible:
msg": "Install was NOT Sucessful; Please Check FortiManager Logs" ... "state": "warning"
I suggest a condition should be added to the API result validation to not fail and just return "changed=False"
if install["result"][0]["status"]["code"] == 0 and install["result"][0]["data"]["state"] == "done": results = dict(install=install, changed=True) else: module.fail_json(**dict(status=install, msg="Install was NOT Sucessful; Please Check FortiManager Logs"))
Hello,
I have tried to use another module but I still get multiple errors
ansible-playbook fortimgr_policy_unittest.yml
PLAY [CREATE SESSION ID FOR UNIT TESTS] ***************************************************************************************************************************************************************************
TASK [FORTIMANAGER LOCK - CHANGE] *********************************************************************************************************************************************************************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: requests.exceptions.ConnectionError: HTTPSConnectionPool(host='fmg1', port=443): Max retries exceeded with url: /jsonrpc (Caused by NewConnectionError('<urllib3.connection.VerifiedHTTPSConnection object at 0x7f3889f1af90>: Failed to establish a new connection: [Errno -2] Name or service not known',))
fatal: [fmg1]: FAILED! => {"changed": false, "module_stderr": "Traceback (most recent call last):\n File "/root/.ansible/tmp/ansible-tmp-1545237939.06-109451512231250/AnsiballZ_fortimgr_lock.py", line 113, in \n _ansiballz_main()\n File "/root/.ansible/tmp/ansible-tmp-1545237939.06-109451512231250/AnsiballZ_fortimgr_lock.py", line 105, in _ansiballz_main\n invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n File "/root/.ansible/tmp/ansible-tmp-1545237939.06-109451512231250/AnsiballZ_fortimgr_lock.py", line 48, in invoke_module\n imp.load_module('main', mod, module, MOD_DESC)\n File "/tmp/ansible_fortimgr_lock_payload_Aqyqj7/main.py", line 1494, in \n File "/tmp/ansible_fortimgr_lock_payload_Aqyqj7/main.py", line 1460, in main\n File "/tmp/ansible_fortimgr_lock_payload_Aqyqj7/main.py", line 1225, in login\n File "/tmp/ansible_fortimgr_lock_payload_Aqyqj7/main.py", line 1253, in make_request\n File "/usr/local/lib/python2.7/dist-packages/requests/api.py", line 116, in post\n return request('post', url, data=data, json=json, **kwargs)\n File "/usr/local/lib/python2.7/dist-packages/requests/api.py", line 60, in request\n return session.request(method=method, url=url, **kwargs)\n File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 533, in request\n resp = self.send(prep, **send_kwargs)\n File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 646, in send\n r = adapter.send(request, **kwargs)\n File "/usr/local/lib/python2.7/dist-packages/requests/adapters.py", line 516, in send\n raise ConnectionError(e, request=request)\nrequests.exceptions.ConnectionError: HTTPSConnectionPool(host='fmg1', port=443): Max retries exceeded with url: /jsonrpc (Caused by NewConnectionError('<urllib3.connection.VerifiedHTTPSConnection object at 0x7f3889f1af90>: Failed to establish a new connection: [Errno -2] Name or service not known',))\n", "module_stdout": "", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}
to retry, use: --limit @/home/tubalcain/depot/fortimanager-ansible/unittests/fortimgr_policy_unittest.retry
PLAY RECAP ********************************************************************************************************************************************************************************************************
fmg1 : ok=0 changed=0 unreachable=0 failed=1
I have modified my inventory to get the same variable than the module :
[fortimanager]
fmg1 inventory_hostname=10.5.21.232 username=admin password=fortinet
could you let me know how to use this module ?
regards,
When installing a policy_package with fortimgr_install, the "fortigate_name" parameter specifying the device on which to install the policy isn't respected and the Fortimanager actually pushes to all devices.
This seems to be because the Fortigate device specified with "fortigate_name" is passed as a list item for the "scope" parameter to the API endpoint (/securityconsole/install/package)
args = dict(
adom=adom,
adom_rev_comments=module.params["adom_revision_comments"],
adom_rev_name=module.params["adom_revision_name"],
dev_rev_comments=module.params["fortigate_revision_comments"],
flags=module.params["install_flags"],
pkg=package,
scope=[fortigate]
)
The scope parameter, according to the API doc should be
scope object, refer to device object, or group object
Target device or device group.
"object member": [ { "name": "...", "vdom": "..." }, { "name": "..." }, ... ]
When referencing to a device, the object should contain both "name" and "vdom".
When referencing to other objects (ie. device groups), only the name attribute is used.
The API behaviour with the current format of the scope parameter seems to be to revert to push to all Fortigate devices.
(Tested on FortiManager 5.6.8)
Summary:
I have an Fortimanager VM with trail license running on it. I'm trying to configure address in device using fmgr_fwobj_address module, but it is not working. I have super user access to Fortimanager and able to do SSH from Ansible server.
Ansible version:
ansible 2.9.1
config file = /etc/ansible/ansible.cfg
configured module search path = [u'/home/sysadmin/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python2.7/dist-packages/ansible
executable location = /usr/bin/ansible
python version = 2.7.12 (default, Nov 12 2018, 14:36:49) [GCC 5.4.0 20160609]
STEPS TO REPRODUCE
Playbook:
Inventory file:
[FortiManager]
172.20.32.200 ansible_host=172.20.32.200
[fmgr_api:children]
FortiManager
[fmgr_api:vars]
ansible_network_os=fortimanager
ansible_user=admin
ansible_password=admin
ansible_become=no
ansible_become_method=disable
ansible_httpapi_use_ssl=true
ansible_httpapi_validate_certs=false
ansible_httpapi_timeout=300
EXPECTED RESULTS:
The playbook should configure the address in Fortimanager
ACTUAL RESULTS:
fatal: [172.20.32.200]: FAILED! => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"module_stderr": "Traceback (most recent call last):\n File "/home/sysadmin/.ansible/tmp/ansible-local-252351JU_df/ansible-tmp-1574851380.58-52172427055422/AnsiballZ_fmgr_fwobj_address.py", line 102, in \n _ansiballz_main()\n File "/home/sysadmin/.ansible/tmp/ansible-local-252351JU_df/ansible-tmp-1574851380.58-52172427055422/AnsiballZ_fmgr_fwobj_address.py", line 94, in _ansiballz_main\n invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n File "/home/sysadmin/.ansible/tmp/ansible-local-252351JU_df/ansible-tmp-1574851380.58-52172427055422/AnsiballZ_fmgr_fwobj_address.py", line 40, in invoke_module\n runpy.run_module(mod_name='ansible.modules.network.fortimanager.fmgr_fwobj_address', init_globals=None, run_name='main', alter_sys=True)\n File "/usr/lib/python2.7/runpy.py", line 188, in run_module\n fname, loader, pkg_name)\n File "/usr/lib/python2.7/runpy.py", line 82, in _run_module_code\n mod_name, mod_fname, mod_loader, pkg_name)\n File "/usr/lib/python2.7/runpy.py", line 72, in _run_code\n exec code in run_globals\n File "/tmp/ansible_fmgr_fwobj_address_payload_gjnAbb/ansible_fmgr_fwobj_address_payload.zip/ansible/modules/network/fortimanager/fmgr_fwobj_address.py", line 668, in \n File "/tmp/ansible_fmgr_fwobj_address_payload_gjnAbb/ansible_fmgr_fwobj_address_payload.zip/ansible/modules/network/fortimanager/fmgr_fwobj_address.py", line 658, in main\nansible.module_utils.network.fortimanager.common.FMGBaseException: An attempt was made at communicating with a FMG with no valid session and an unexpected error was discovered.\n",
"module_stdout": "",
"msg": "MODULE FAILURE\nSee stdout/stderr for the exact error",
"rc": 1
Sorry to mark this as an issue, would you please attach a LICENSE file for this project? For user to properly use or distribute these modules? I know on every module python file, there is a header with license term, it would be great if you can attach a license file I think. I noticed on other projects from your NTC company, you use Apache license instead of GNU license. Thanks!
Team ,
any idea about the below error
he full traceback is:
Traceback (most recent call last):
File "/root/.ansible/tmp/ansible-tmp-1558110708.74-201937228005129/AnsiballZ_fortimgr_address_map.py", line 114, in <module>
_ansiballz_main()
File "/root/.ansible/tmp/ansible-tmp-1558110708.74-201937228005129/AnsiballZ_fortimgr_address_map.py", line 106, in _ansiballz_main
invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)
File "/root/.ansible/tmp/ansible-tmp-1558110708.74-201937228005129/AnsiballZ_fortimgr_address_map.py", line 49, in invoke_module
imp.load_module('__main__', mod, module, MOD_DESC)
File "/tmp/ansible_fortimgr_address_map_payload_aJnZJP/__main__.py", line 296, in <module>
ImportError: cannot import name return_values
failed: [] (item={u'subnet': u'10.10.100.10/32', u'type': u'subnet', u'name': u'db01', u'vdom': u'prod', u'fg': u'prod_dmz'}) => {
"ansible_loop_var": "item",
"changed": false,
"item": {
"fg": "prod_dmz",
"name": "db01",
"subnet": "10.10.100.10/32",
"type": "subnet",
"vdom": "prod"
},
"module_stderr": "Traceback (most recent call last):\n File \"/root/.ansible/tmp/ansible-tmp-1558110708.74-201937228005129/AnsiballZ_fortimgr_address_map.py\", line 114, in <module>\n _ansiballz_main()\n File \"/root/.ansible/tmp/ansible-tmp-1558110708.74-201937228005129/AnsiballZ_fortimgr_address_map.py\", line 106, in _ansiballz_main\n invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n File \"/root/.ansible/tmp/ansible-tmp-1558110708.74-201937228005129/AnsiballZ_fortimgr_address_map.py\", line 49, in invoke_module\n imp.load_module('__main__', mod, module, MOD_DESC)\n File \"/tmp/ansible_fortimgr_address_map_payload_aJnZJP/__main__.py\", line 296, in <module>\nImportError: cannot import name return_values\n",
"module_stdout": "",
"msg": "MODULE FAILURE\nSee stdout/stderr for the exact error",
"rc": 1
}
fatal: []: FAILED! => {
"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'subnet'\n\nThe error appears to be in '/root/fortigate/fortimanager-ansible-master/test.yml': line 8, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n tasks:\n - name: CONFIGURE ADDRESS MAPPINGS\n ^ here\n"
}
Hi,
The last time I ran a playbook and it failed I noticed that the session was left hanging on the fortimanager GUI. The session was idle there for, at least, 10m and then I deleted it manually.
I'm happy to take point in solving this issue, I just wanted to confirm that more people are able to reproduce this. This occurred with the install policy module (fortimgr_install)
Thanks!
Greetings,
I am in desperate need for an ansible module to apply Security profiles and groups to rules which match a destination address or interface. I cannot seem to find this as part of this module, or anything security related. Is this possible? If not, perhaps a feature request (which I can help put hours against) ?
Is the intended behavior of the “present” state to only add/update parameters passed to the module and leave all other existing parameters untouched or are the parameters passed meant to be represent the complete “desired state” of the object, meaning that existing object parameters not passed to the module should be removed?
I expected the second but my actual results and reading of the code lead me to believe it’s the first...
fortimgr_lock module documentation (https://github.com/networktocode/fortimanager-ansible/blob/master/Module_Docs/fortimgr_module_docs.md#fortimgr_lock) list the "save_config" parameter but the code looks for the "save" parameter.
Hello,
at first great work.
Can you change your code, so we can push objects to the global adom. Something like this is working for me:
if self.adom != "global":
self.dvmdb_url = "/dvmdb/adom/{}/".format(self.adom)
self.obj_url = "/pm/config/adom/{}/obj/firewall/{}".format(self.adom, self.api_endpoint)
self.pkg_url = "/pm/config/adom/{}/pkg/{}/firewall/{}".format(self.adom, self.package, self.api_endpoint)
self.wsp_url = "/dvmdb/adom/{}/workspace/".format(self.adom)
else:
self.dvmdb_url = "/dvmdb/global/"
self.obj_url = "/pm/config/global/obj/firewall/{}".format(self.api_endpoint)
self.pkg_url = "/pm/config/global/pkg/{}/firewall/{}".format(self.package, self.api_endpoint)
self.wsp_url = "/dvmdb/global/workspace/"
I don't know if you need this line for global:
self.dvmdb_url = "/dvmdb/global/"
And i think the URL should be changed to:
self.url = "https://{fw}:{port}/jsonrpc".format(fw=self.host, port=self.port)
Thank you for sharing this module
When the FortiManager does not support policy name feature, and the policy_name parameter is passed to the fortimgr_policy, the module fails on accessing data
key in the json result from get_iem_from_name()
. Need to add a check for data and fail the module if there is an error with the response from FortiManager.
hello,
Using the fortmgr_route.yml , I have the following error when executing it 👍
ansible-playbook fortmgr_route.yml
root@tubalcain:/home/tubalcain/depot/fortimanager-ansible/examples# ansible-playbook fortmgr_route.yml
PLAY [GET FACTS FROM FORTIMANAGER] *********************************************
TASK [Add Route] ***************************************************************
fatal: [10.5.21.253]: FAILED! => {"msg": "'routes' is undefined"}
to retry, use: --limit @/home/tubalcain/depot/fortimanager-ansible/examples/fortmgr_route.retry
PLAY RECAP *********************************************************************
10.5.21.253 : ok=0 changed=0 unreachable=0 failed=1
thanks
Hello @jmcgill298 gerat work !
I have fresh fortimanager vm 5.2.9 and your modules are not working.
Many times, you use
if not session_login.json()["result"][0]["status"]["code"] == 0:
And for me result is not a list, just a single dict.
What's your env ?
I see lot of code duplication, don't you want to create a single module_util file and point to it with your modules ?
I'm very interested in fortimanager with ansible, maybe I can help you ?
- name: ensure inexistant policy is absent
fortimgr_policy:
host: "{{ fortimanager }}"
session_id: "{{ session.session_id }}"
adom: "{{ adom_name }}"
state: absent
package: "{{ policy_package_name }}"
policy_name: "inexistant-policy-name"
fatal: [localhost]: FAILED! => {
"changed": false,
"failed": true,
"module_stderr": "/usr/lib/python2.7/site-packages/urllib3/connectionpool.py:852: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings\n InsecureRequestWarning)\nTraceback (most recent call last):\n File \"/tmp/ansible_GLEqr9/ansible_module_fortimgr_policy.py\", line 2395, in <module>\n main()\n File \"/tmp/ansible_GLEqr9/ansible_module_fortimgr_policy.py\", line 2369, in main\n policy_id = results[\"config\"][\"id\"]\nKeyError: 'id'\n",
"module_stdout": "",
"rc": 0
}
MSG:
MODULE FAILURE
When using a folders in policy package management, the module fortimgr_policy fails.
VARS:
vip_list.package: "folder/policy_package_name"
^^^ Note the var contain a "/".
PLAYBOOK:
- name: Create policy for VIP
fortimgr_policy:
<<: *fmg_login
package: "{{ vip_list.package }}"
PLAYBOOK RUN
TASK [Create policy for VIP] *****************************************************************************************************************************************************************
Friday 26 April 2019 19:09:23 +0000 (0:00:01.709) 0:00:03.787 **********
fatal: [fortigate.domain.com]: FAILED! => changed=false
fortimanager_response:
result:
- status:
code: -3
message: Object does not exist
url: /pm/config/adom/vdom/pkg/folder\/policy_package_name/firewall/policy
msg: Unable to Apply Config
^^^ As you can see, when running the Playbook, the module add a backslash in front of the slash (it adds an escape character).
We've been able to "fix" the problem by replacing a line of code in fortimgr_policy.py file. We basically got rid of the "/" to "/" replacement.
fortimgr_policy.py (start at line 1566):
def _escape_params_url(self, url):
"""
This private method is used to escape slash ("/") characters from a url string to be provided as a json-rpc request params.
Slash characters are escaped by prefixing with a backslash ("\").
If url is None, None is returned.
:param url: Type str.
The url string to process.
:return: The url string with slash characters escaped with a backslash ("\") or None if url is None.
"""
if url is not None:
#return str(url).replace('/', '\\/') <---- Original line
return str(url) # <---- Temporary Fix!
else:
return None
In my opinion, the module should support folders. The best solution would probably be to add a folder parameter to the module. So, you could still do the "escaping" while being able to add folders.
- name: Create policy
fortimgr_policy:
folder: "{{ package_folder }}"
package: "{{ package_name }}"
In Python:
return str(folder)+"/"+str(url).replace('/', '\\/')
instead of
return str(url).replace('/', '\\/')
Hey!
Appreciate the modules so far, easy to work with.
However, I'm running into an issue with fortimgr_jsonrpc_request. I'm running ansible 2.6
I'm running a playbook with a var file.
playbook:
- name: Create loopback interface
fortimgr_jsonrpc_request:
<<: *fmg_login
method: "add"
params: [{
url: "/pm/config/device/{{ address_list.fortigate_name }}/vdom/{{ address_list.vdom_name }}/system/interface/",
data: [{
name: "{{ address_list.loopback_name }}",
type: "loopback",
ip: "{{ address_list.public_ip }}",
allowaccess: "ping"
}]
}]
tags: interface
var:
adom: TestAPI
vdom_name: TEST
fortigate_name: lab
public_ip: 1.1.1.1
loopback_name: LO-TEST
command:
ansible-playbook -l hidden playbooks/fortimanager/fmg.yml --ask-vault-pass -t interface
response:
changed: [pdc-onl-fmg01.ubisoft.onbe] => {
"changed": true,
"invocation": {
"module_args": {
"adom": "TestAPI",
"host": "hidden",
"lock": false,
"method": "add",
"params": [
{
"data": [
{
"allowaccess": "ping",
"ip": "1.1.1.1/32",
"name": "LO-TEST",
"type": "loopback"
}
],
"url": "/pm/config/device/lab/vdom/TEST/system/interface/"
}
],
"password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"port": null,
"provider": null,
"session_id": null,
"use_ssl": null,
"username": "fmg",
"validate_certs": null
}
},
"response": [
{
"result": [
{
"status": {
"code": 0,
"message": "OK"
},
"url": "/pm/config/device/lab/vdom/TEST/system/interface/"
}
]
},
[
{
"data": [
{
"allowaccess": "ping",
"ip": "1.1.1.1/32",
"name": "LO-TEST",
"type": "loopback"
}
],
"url": "/pm/config/device/lab/vdom/TEST/system/interface/"
}
]
],
"status": {
"code": 0,
"message": "OK"
}
}
So from the response, it looks like it should've worked and a new loopback interface should've been added to "lab" fortigate in the "TEST" vdom, but if I go to my fortimanager and check in the ADOM and VDOM, there's no new interface added.
If I rerun this playbook multiple times, it still shows as "changed" but nothing is there. I know the formatting is correct because I tried putting bogus information and it spits out an error saying "invalid url".
Any ideas?
Hi...I want to contribute with you. I can create a logo for you. Do you want it?
Hello,
I've used fortimgr_facts
to get configuration of FortiGate managed by FortiManager, however I get it in dictionary format of Ansible. Is it possible to get the output in the same format as you would get when you issues show
command on FortiGate? I would like to use Ansible to download the configurations of FortiGates from FortiManager (I can access the configuration via UI in Device Manager -> Revision History -> Show Configuration). When I get this output I can use it to restore configuration in case of changing hardware.
Thanks.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.