We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[META 388] Collect Azure App Service cloud metadata
Implement https://github.com/elastic/apm/issues/388

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `elasticapm/utils/cloud.py`
Content:
```
1 #  BSD 3-Clause License
2 #
3 #  Copyright (c) 2019, Elasticsearch BV
4 #  All rights reserved.
5 #
6 #  Redistribution and use in source and binary forms, with or without
7 #  modification, are permitted provided that the following conditions are met:
8 #
9 #  * Redistributions of source code must retain the above copyright notice, this
10 #    list of conditions and the following disclaimer.
11 #
12 #  * Redistributions in binary form must reproduce the above copyright notice,
13 #    this list of conditions and the following disclaimer in the documentation
14 #    and/or other materials provided with the distribution.
15 #
16 #  * Neither the name of the copyright holder nor the names of its
17 #    contributors may be used to endorse or promote products derived from
18 #    this software without specific prior written permission.
19 #
20 #  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 #  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 #  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 #  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 #  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 #  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 #  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 #  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 #  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 #  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 import json
32 import os
33 import socket
34 
35 import urllib3
36 
37 
38 def aws_metadata():
39     """
40     Fetch AWS metadata from the local metadata server. If metadata server is
41     not found, return an empty dictionary
42     """
43     http = urllib3.PoolManager()
44 
45     try:
46         # This will throw an error if the metadata server isn't available,
47         # and will be quiet in the logs, unlike urllib3
48         socket.create_connection(("169.254.169.254", 80), 0.1)
49 
50         try:
51             # This whole block is almost unnecessary. IMDSv1 will be supported
52             # indefinitely, so the only time this block is needed is if a
53             # security-conscious user has set the metadata service to require
54             # IMDSv2. Thus, the very expansive try:except: coverage.
55 
56             # TODO: should we have a config option to completely disable IMDSv2 to reduce overhead?
57             ttl_header = {"X-aws-ec2-metadata-token-ttl-seconds": "300"}
58             token_url = "http://169.254.169.254/latest/api/token"
59             token_request = http.request("PUT", token_url, headers=ttl_header, timeout=1.0, retries=False)
60             token = token_request.data.decode("utf-8")
61             aws_token_header = {"X-aws-ec2-metadata-token": token} if token else {}
62         except Exception:
63             aws_token_header = {}
64         metadata = json.loads(
65             http.request(
66                 "GET",
67                 "http://169.254.169.254/latest/dynamic/instance-identity/document",
68                 headers=aws_token_header,
69                 timeout=1.0,
70                 retries=False,
71             ).data.decode("utf-8")
72         )
73 
74         return {
75             "account": {"id": metadata["accountId"]},
76             "instance": {"id": metadata["instanceId"]},
77             "availability_zone": metadata["availabilityZone"],
78             "machine": {"type": metadata["instanceType"]},
79             "provider": "aws",
80             "region": metadata["region"],
81         }
82 
83     except Exception:
84         # Not on an AWS box
85         return {}
86 
87 
88 def gcp_metadata():
89     """
90     Fetch GCP metadata from the local metadata server. If metadata server is
91     not found, return an empty dictionary
92     """
93     headers = {"Metadata-Flavor": "Google"}
94     http = urllib3.PoolManager()
95 
96     try:
97         # This will throw an error if the metadata server isn't available,
98         # and will be quiet in the logs, unlike urllib3
99         socket.getaddrinfo("metadata.google.internal", 80, 0, socket.SOCK_STREAM)
100 
101         metadata = json.loads(
102             http.request(
103                 "GET",
104                 "http://metadata.google.internal/computeMetadata/v1/?recursive=true",
105                 headers=headers,
106                 timeout=1.0,
107                 retries=False,
108             ).data.decode("utf-8")
109         )
110 
111         availability_zone = os.path.split(metadata["instance"]["zone"])[1]
112 
113         return {
114             "provider": "gcp",
115             "instance": {"id": str(metadata["instance"]["id"]), "name": metadata["instance"]["name"]},
116             "project": {"id": str(metadata["project"]["numericProjectId"]), "name": metadata["project"]["projectId"]},
117             "availability_zone": availability_zone,
118             "region": availability_zone.rsplit("-", 1)[0],
119             "machine": {"type": metadata["instance"]["machineType"].split("/")[-1]},
120         }
121 
122     except Exception:
123         # Not on a gcp box
124         return {}
125 
126 
127 def azure_metadata():
128     """
129     Fetch Azure metadata from the local metadata server. If metadata server is
130     not found, return an empty dictionary
131     """
132     headers = {"Metadata": "true"}
133     http = urllib3.PoolManager()
134 
135     try:
136         # This will throw an error if the metadata server isn't available,
137         # and will be quiet in the logs, unlike urllib3
138         socket.create_connection(("169.254.169.254", 80), 0.1)
139 
140         # Can't use newest metadata service version, as it's not guaranteed
141         # to be available in all regions
142         metadata = json.loads(
143             http.request(
144                 "GET",
145                 "http://169.254.169.254/metadata/instance/compute?api-version=2019-08-15",
146                 headers=headers,
147                 timeout=1.0,
148                 retries=False,
149             ).data.decode("utf-8")
150         )
151 
152         ret = {
153             "account": {"id": metadata["subscriptionId"]},
154             "instance": {"id": metadata["vmId"], "name": metadata["name"]},
155             "project": {"name": metadata["resourceGroupName"]},
156             "availability_zone": metadata["zone"],
157             "machine": {"type": metadata["vmSize"]},
158             "provider": "azure",
159             "region": metadata["location"],
160         }
161 
162         if not ret["availability_zone"]:
163             ret.pop("availability_zone")
164         return ret
165 
166     except Exception:
167         # Not on an Azure box
168         return {}
169
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/elasticapm/utils/cloud.py b/elasticapm/utils/cloud.py
--- a/elasticapm/utils/cloud.py
+++ b/elasticapm/utils/cloud.py
@@ -164,5 +164,30 @@
         return ret
 
     except Exception:
-        # Not on an Azure box
+        # Not on an Azure box, maybe an azure app service?
+        return azure_app_service_metadata()
+
+
+def azure_app_service_metadata():
+    ret = {"provider": "azure"}
+    website_owner_name = os.environ.get("WEBSITE_OWNER_NAME")
+    website_instance_id = os.environ.get("WEBSITE_INSTANCE_ID")
+    website_site_name = os.environ.get("WEBSITE_SITE_NAME")
+    website_resource_group = os.environ.get("WEBSITE_RESOURCE_GROUP")
+    if not all((website_owner_name, website_instance_id, website_site_name, website_resource_group)):
+        return {}
+    # Format of website_owner_name: {subscription id}+{app service plan resource group}-{region}webspace{.*}
+    if "+" not in website_owner_name:
+        return {}
+    try:
+        account_id, website_owner_name = website_owner_name.split("+")
+        ret["account"] = {"id": account_id}
+        region, _ = website_owner_name.split("webspace")
+        ret["region"] = region.rsplit("-", 1)[1]
+    except Exception:
         return {}
+
+    ret["instance"] = {"id": website_instance_id, "name": website_site_name}
+    ret["project"] = {"name": website_resource_group}
+
+    return ret

{"golden_diff": "diff --git a/elasticapm/utils/cloud.py b/elasticapm/utils/cloud.py\n--- a/elasticapm/utils/cloud.py\n+++ b/elasticapm/utils/cloud.py\n@@ -164,5 +164,30 @@\n         return ret\n \n     except Exception:\n-        # Not on an Azure box\n+        # Not on an Azure box, maybe an azure app service?\n+        return azure_app_service_metadata()\n+\n+\n+def azure_app_service_metadata():\n+    ret = {\"provider\": \"azure\"}\n+    website_owner_name = os.environ.get(\"WEBSITE_OWNER_NAME\")\n+    website_instance_id = os.environ.get(\"WEBSITE_INSTANCE_ID\")\n+    website_site_name = os.environ.get(\"WEBSITE_SITE_NAME\")\n+    website_resource_group = os.environ.get(\"WEBSITE_RESOURCE_GROUP\")\n+    if not all((website_owner_name, website_instance_id, website_site_name, website_resource_group)):\n+        return {}\n+    # Format of website_owner_name: {subscription id}+{app service plan resource group}-{region}webspace{.*}\n+    if \"+\" not in website_owner_name:\n+        return {}\n+    try:\n+        account_id, website_owner_name = website_owner_name.split(\"+\")\n+        ret[\"account\"] = {\"id\": account_id}\n+        region, _ = website_owner_name.split(\"webspace\")\n+        ret[\"region\"] = region.rsplit(\"-\", 1)[1]\n+    except Exception:\n         return {}\n+\n+    ret[\"instance\"] = {\"id\": website_instance_id, \"name\": website_site_name}\n+    ret[\"project\"] = {\"name\": website_resource_group}\n+\n+    return ret\n", "issue": "[META 388] Collect Azure App Service cloud metadata\nImplement https://github.com/elastic/apm/issues/388\n", "before_files": [{"content": "#  BSD 3-Clause License\n#\n#  Copyright (c) 2019, Elasticsearch BV\n#  All rights reserved.\n#\n#  Redistribution and use in source and binary forms, with or without\n#  modification, are permitted provided that the following conditions are met:\n#\n#  * Redistributions of source code must retain the above copyright notice, this\n#    list of conditions and the following disclaimer.\n#\n#  * Redistributions in binary form must reproduce the above copyright notice,\n#    this list of conditions and the following disclaimer in the documentation\n#    and/or other materials provided with the distribution.\n#\n#  * Neither the name of the copyright holder nor the names of its\n#    contributors may be used to endorse or promote products derived from\n#    this software without specific prior written permission.\n#\n#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n#  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n#  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n#  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n#  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n#  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n#  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n#  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nimport json\nimport os\nimport socket\n\nimport urllib3\n\n\ndef aws_metadata():\n    \"\"\"\n    Fetch AWS metadata from the local metadata server. If metadata server is\n    not found, return an empty dictionary\n    \"\"\"\n    http = urllib3.PoolManager()\n\n    try:\n        # This will throw an error if the metadata server isn't available,\n        # and will be quiet in the logs, unlike urllib3\n        socket.create_connection((\"169.254.169.254\", 80), 0.1)\n\n        try:\n            # This whole block is almost unnecessary. IMDSv1 will be supported\n            # indefinitely, so the only time this block is needed is if a\n            # security-conscious user has set the metadata service to require\n            # IMDSv2. Thus, the very expansive try:except: coverage.\n\n            # TODO: should we have a config option to completely disable IMDSv2 to reduce overhead?\n            ttl_header = {\"X-aws-ec2-metadata-token-ttl-seconds\": \"300\"}\n            token_url = \"http://169.254.169.254/latest/api/token\"\n            token_request = http.request(\"PUT\", token_url, headers=ttl_header, timeout=1.0, retries=False)\n            token = token_request.data.decode(\"utf-8\")\n            aws_token_header = {\"X-aws-ec2-metadata-token\": token} if token else {}\n        except Exception:\n            aws_token_header = {}\n        metadata = json.loads(\n            http.request(\n                \"GET\",\n                \"http://169.254.169.254/latest/dynamic/instance-identity/document\",\n                headers=aws_token_header,\n                timeout=1.0,\n                retries=False,\n            ).data.decode(\"utf-8\")\n        )\n\n        return {\n            \"account\": {\"id\": metadata[\"accountId\"]},\n            \"instance\": {\"id\": metadata[\"instanceId\"]},\n            \"availability_zone\": metadata[\"availabilityZone\"],\n            \"machine\": {\"type\": metadata[\"instanceType\"]},\n            \"provider\": \"aws\",\n            \"region\": metadata[\"region\"],\n        }\n\n    except Exception:\n        # Not on an AWS box\n        return {}\n\n\ndef gcp_metadata():\n    \"\"\"\n    Fetch GCP metadata from the local metadata server. If metadata server is\n    not found, return an empty dictionary\n    \"\"\"\n    headers = {\"Metadata-Flavor\": \"Google\"}\n    http = urllib3.PoolManager()\n\n    try:\n        # This will throw an error if the metadata server isn't available,\n        # and will be quiet in the logs, unlike urllib3\n        socket.getaddrinfo(\"metadata.google.internal\", 80, 0, socket.SOCK_STREAM)\n\n        metadata = json.loads(\n            http.request(\n                \"GET\",\n                \"http://metadata.google.internal/computeMetadata/v1/?recursive=true\",\n                headers=headers,\n                timeout=1.0,\n                retries=False,\n            ).data.decode(\"utf-8\")\n        )\n\n        availability_zone = os.path.split(metadata[\"instance\"][\"zone\"])[1]\n\n        return {\n            \"provider\": \"gcp\",\n            \"instance\": {\"id\": str(metadata[\"instance\"][\"id\"]), \"name\": metadata[\"instance\"][\"name\"]},\n            \"project\": {\"id\": str(metadata[\"project\"][\"numericProjectId\"]), \"name\": metadata[\"project\"][\"projectId\"]},\n            \"availability_zone\": availability_zone,\n            \"region\": availability_zone.rsplit(\"-\", 1)[0],\n            \"machine\": {\"type\": metadata[\"instance\"][\"machineType\"].split(\"/\")[-1]},\n        }\n\n    except Exception:\n        # Not on a gcp box\n        return {}\n\n\ndef azure_metadata():\n    \"\"\"\n    Fetch Azure metadata from the local metadata server. If metadata server is\n    not found, return an empty dictionary\n    \"\"\"\n    headers = {\"Metadata\": \"true\"}\n    http = urllib3.PoolManager()\n\n    try:\n        # This will throw an error if the metadata server isn't available,\n        # and will be quiet in the logs, unlike urllib3\n        socket.create_connection((\"169.254.169.254\", 80), 0.1)\n\n        # Can't use newest metadata service version, as it's not guaranteed\n        # to be available in all regions\n        metadata = json.loads(\n            http.request(\n                \"GET\",\n                \"http://169.254.169.254/metadata/instance/compute?api-version=2019-08-15\",\n                headers=headers,\n                timeout=1.0,\n                retries=False,\n            ).data.decode(\"utf-8\")\n        )\n\n        ret = {\n            \"account\": {\"id\": metadata[\"subscriptionId\"]},\n            \"instance\": {\"id\": metadata[\"vmId\"], \"name\": metadata[\"name\"]},\n            \"project\": {\"name\": metadata[\"resourceGroupName\"]},\n            \"availability_zone\": metadata[\"zone\"],\n            \"machine\": {\"type\": metadata[\"vmSize\"]},\n            \"provider\": \"azure\",\n            \"region\": metadata[\"location\"],\n        }\n\n        if not ret[\"availability_zone\"]:\n            ret.pop(\"availability_zone\")\n        return ret\n\n    except Exception:\n        # Not on an Azure box\n        return {}\n", "path": "elasticapm/utils/cloud.py"}], "after_files": [{"content": "#  BSD 3-Clause License\n#\n#  Copyright (c) 2019, Elasticsearch BV\n#  All rights reserved.\n#\n#  Redistribution and use in source and binary forms, with or without\n#  modification, are permitted provided that the following conditions are met:\n#\n#  * Redistributions of source code must retain the above copyright notice, this\n#    list of conditions and the following disclaimer.\n#\n#  * Redistributions in binary form must reproduce the above copyright notice,\n#    this list of conditions and the following disclaimer in the documentation\n#    and/or other materials provided with the distribution.\n#\n#  * Neither the name of the copyright holder nor the names of its\n#    contributors may be used to endorse or promote products derived from\n#    this software without specific prior written permission.\n#\n#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n#  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n#  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n#  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n#  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n#  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n#  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n#  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nimport json\nimport os\nimport socket\n\nimport urllib3\n\n\ndef aws_metadata():\n    \"\"\"\n    Fetch AWS metadata from the local metadata server. If metadata server is\n    not found, return an empty dictionary\n    \"\"\"\n    http = urllib3.PoolManager()\n\n    try:\n        # This will throw an error if the metadata server isn't available,\n        # and will be quiet in the logs, unlike urllib3\n        socket.create_connection((\"169.254.169.254\", 80), 0.1)\n\n        try:\n            # This whole block is almost unnecessary. IMDSv1 will be supported\n            # indefinitely, so the only time this block is needed is if a\n            # security-conscious user has set the metadata service to require\n            # IMDSv2. Thus, the very expansive try:except: coverage.\n\n            # TODO: should we have a config option to completely disable IMDSv2 to reduce overhead?\n            ttl_header = {\"X-aws-ec2-metadata-token-ttl-seconds\": \"300\"}\n            token_url = \"http://169.254.169.254/latest/api/token\"\n            token_request = http.request(\"PUT\", token_url, headers=ttl_header, timeout=1.0, retries=False)\n            token = token_request.data.decode(\"utf-8\")\n            aws_token_header = {\"X-aws-ec2-metadata-token\": token} if token else {}\n        except Exception:\n            aws_token_header = {}\n        metadata = json.loads(\n            http.request(\n                \"GET\",\n                \"http://169.254.169.254/latest/dynamic/instance-identity/document\",\n                headers=aws_token_header,\n                timeout=1.0,\n                retries=False,\n            ).data.decode(\"utf-8\")\n        )\n\n        return {\n            \"account\": {\"id\": metadata[\"accountId\"]},\n            \"instance\": {\"id\": metadata[\"instanceId\"]},\n            \"availability_zone\": metadata[\"availabilityZone\"],\n            \"machine\": {\"type\": metadata[\"instanceType\"]},\n            \"provider\": \"aws\",\n            \"region\": metadata[\"region\"],\n        }\n\n    except Exception:\n        # Not on an AWS box\n        return {}\n\n\ndef gcp_metadata():\n    \"\"\"\n    Fetch GCP metadata from the local metadata server. If metadata server is\n    not found, return an empty dictionary\n    \"\"\"\n    headers = {\"Metadata-Flavor\": \"Google\"}\n    http = urllib3.PoolManager()\n\n    try:\n        # This will throw an error if the metadata server isn't available,\n        # and will be quiet in the logs, unlike urllib3\n        socket.getaddrinfo(\"metadata.google.internal\", 80, 0, socket.SOCK_STREAM)\n\n        metadata = json.loads(\n            http.request(\n                \"GET\",\n                \"http://metadata.google.internal/computeMetadata/v1/?recursive=true\",\n                headers=headers,\n                timeout=1.0,\n                retries=False,\n            ).data.decode(\"utf-8\")\n        )\n\n        availability_zone = os.path.split(metadata[\"instance\"][\"zone\"])[1]\n\n        return {\n            \"provider\": \"gcp\",\n            \"instance\": {\"id\": str(metadata[\"instance\"][\"id\"]), \"name\": metadata[\"instance\"][\"name\"]},\n            \"project\": {\"id\": str(metadata[\"project\"][\"numericProjectId\"]), \"name\": metadata[\"project\"][\"projectId\"]},\n            \"availability_zone\": availability_zone,\n            \"region\": availability_zone.rsplit(\"-\", 1)[0],\n            \"machine\": {\"type\": metadata[\"instance\"][\"machineType\"].split(\"/\")[-1]},\n        }\n\n    except Exception:\n        # Not on a gcp box\n        return {}\n\n\ndef azure_metadata():\n    \"\"\"\n    Fetch Azure metadata from the local metadata server. If metadata server is\n    not found, return an empty dictionary\n    \"\"\"\n    headers = {\"Metadata\": \"true\"}\n    http = urllib3.PoolManager()\n\n    try:\n        # This will throw an error if the metadata server isn't available,\n        # and will be quiet in the logs, unlike urllib3\n        socket.create_connection((\"169.254.169.254\", 80), 0.1)\n\n        # Can't use newest metadata service version, as it's not guaranteed\n        # to be available in all regions\n        metadata = json.loads(\n            http.request(\n                \"GET\",\n                \"http://169.254.169.254/metadata/instance/compute?api-version=2019-08-15\",\n                headers=headers,\n                timeout=1.0,\n                retries=False,\n            ).data.decode(\"utf-8\")\n        )\n\n        ret = {\n            \"account\": {\"id\": metadata[\"subscriptionId\"]},\n            \"instance\": {\"id\": metadata[\"vmId\"], \"name\": metadata[\"name\"]},\n            \"project\": {\"name\": metadata[\"resourceGroupName\"]},\n            \"availability_zone\": metadata[\"zone\"],\n            \"machine\": {\"type\": metadata[\"vmSize\"]},\n            \"provider\": \"azure\",\n            \"region\": metadata[\"location\"],\n        }\n\n        if not ret[\"availability_zone\"]:\n            ret.pop(\"availability_zone\")\n        return ret\n\n    except Exception:\n        # Not on an Azure box, maybe an azure app service?\n        return azure_app_service_metadata()\n\n\ndef azure_app_service_metadata():\n    ret = {\"provider\": \"azure\"}\n    website_owner_name = os.environ.get(\"WEBSITE_OWNER_NAME\")\n    website_instance_id = os.environ.get(\"WEBSITE_INSTANCE_ID\")\n    website_site_name = os.environ.get(\"WEBSITE_SITE_NAME\")\n    website_resource_group = os.environ.get(\"WEBSITE_RESOURCE_GROUP\")\n    if not all((website_owner_name, website_instance_id, website_site_name, website_resource_group)):\n        return {}\n    # Format of website_owner_name: {subscription id}+{app service plan resource group}-{region}webspace{.*}\n    if \"+\" not in website_owner_name:\n        return {}\n    try:\n        account_id, website_owner_name = website_owner_name.split(\"+\")\n        ret[\"account\"] = {\"id\": account_id}\n        region, _ = website_owner_name.split(\"webspace\")\n        ret[\"region\"] = region.rsplit(\"-\", 1)[1]\n    except Exception:\n        return {}\n\n    ret[\"instance\"] = {\"id\": website_instance_id, \"name\": website_site_name}\n    ret[\"project\"] = {\"name\": website_resource_group}\n\n    return ret\n", "path": "elasticapm/utils/cloud.py"}]}