coveralls: 
[![Coverage Status](https://coveralls.io/builds/13259107/badge)](https://coveralls.io/builds/13259107)

Coverage decreased (-0.4%) to 95.253% when pulling **5231408d02b3e69aa963d1702af1a18cfdbfbe24 on jacobtomlinson:env-var-config** into **b67d32310da842dddddb9b907f731a03d0562a2f on opsdroid:master**.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/13259374/badge)](https://coveralls.io/builds/13259374)

Coverage decreased (-0.4%) to 95.253% when pulling **f1c3e2f87b7f8c3cb565f8d4054662747b861f47 on jacobtomlinson:env-var-config** into **b67d32310da842dddddb9b907f731a03d0562a2f on opsdroid:master**.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/13259976/badge)](https://coveralls.io/builds/13259976)

Coverage increased (+0.05%) to 95.712% when pulling **4d1c90850d0a42784b4c11c25a97cd7792510ec2 on jacobtomlinson:env-var-config** into **b67d32310da842dddddb9b907f731a03d0562a2f on opsdroid:master**.

diff --git a/docs/configuration-reference.md b/docs/configuration-reference.md
index 2370bf9..cefea9c 100644
--- a/docs/configuration-reference.md
+++ b/docs/configuration-reference.md
@@ -176,3 +176,15 @@ databases:
     repo: https://github.com/username/mymongofork.git
     no-cache: true
 ```
+
+## Environment variables
+
+You can use environment variables in your config. You need to specify the variable in the place of a value.
+
+```yaml
+skills:
+  - name: myawesomeskill
+    somekey: $ENVIRONMENT_VARIABLE
+```
+
+_Note: Your environment variable names must consist of uppercase characters and underscores only. The value must also be just the environment variable, you cannot currently mix env vars inside strings._
\ No newline at end of file
diff --git a/opsdroid/loader.py b/opsdroid/loader.py
index 055b499..73ec3c5 100644
--- a/opsdroid/loader.py
+++ b/opsdroid/loader.py
@@ -6,6 +6,7 @@ import sys
 import shutil
 import subprocess
 import importlib
+import re
 import yaml
 from opsdroid.const import (
     DEFAULT_GIT_URL, MODULES_DIRECTORY, DEFAULT_MODULES_PATH,
@@ -135,6 +136,17 @@ class Loader:
             _LOGGER.info("No configuration files found.")
             config_path = self.create_default_config(DEFAULT_CONFIG_PATH)
 
+        env_var_pattern = re.compile(r'^\$([A-Z_]*)$')
+        yaml.add_implicit_resolver("!envvar", env_var_pattern)
+
+        def envvar_constructor(loader, node):
+            """Yaml parser for env vars."""
+            value = loader.construct_scalar(node)
+            [env_var] = env_var_pattern.match(value).groups()
+            return os.environ[env_var]
+
+        yaml.add_constructor('!envvar', envvar_constructor)
+
         try:
             with open(config_path, 'r') as stream:
                 _LOGGER.info("Loaded config from %s", config_path)

Allow usage of env vars in config
The configuration should be parsed for environment variables when loaded. This would allow for secrets like api keys to be kept outside of the opsdroid configuration.

#### Example

```yaml
connectors:
  - name: slack
    default-room: '#general'
    bot-name: "opsdroid"
    icon-emoji: ":robot:"
    api-token: "$SLACK_API_KEY"
```
In this example `$SLACK_API_KEY` would be replaced with the contents of the environment variable of the same name.
diff --git a/tests/configs/minimal_with_envs.yaml b/tests/configs/minimal_with_envs.yaml
new file mode 100644
index 0000000..ce97849
--- /dev/null
+++ b/tests/configs/minimal_with_envs.yaml
@@ -0,0 +1,1 @@
+test: $ENVVAR
\ No newline at end of file
diff --git a/tests/test_loader.py b/tests/test_loader.py
index ae78a18..fcb2fbc 100644
--- a/tests/test_loader.py
+++ b/tests/test_loader.py
@@ -29,6 +29,13 @@ class TestLoader(unittest.TestCase):
         config = loader.load_config_file(["tests/configs/minimal.yaml"])
         self.assertIsNotNone(config)
 
+    def test_load_config_file_with_env_vars(self):
+        opsdroid, loader = self.setup()
+        os.environ["ENVVAR"] = 'test'
+        config = loader.load_config_file(
+            ["tests/configs/minimal_with_envs.yaml"])
+        self.assertEqual(config["test"], os.environ["ENVVAR"])
+
     def test_create_default_config(self):
         test_config_path = "/tmp/test_config_path/configuration.yaml"
         opsdroid, loader = self.setup()

pip install -e .
parse_log_pytest
requirements.txt
pytest pytest-cov pytest-xdist
apt-get update apt-get install -y git
3.6
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
aiohttp==2.1.0
arrow==0.10.0
async-timeout==4.0.2
attrs==22.2.0
certifi==2021.5.30
chardet==5.0.0
coverage==6.2
execnet==1.9.0
idna==3.10
importlib-metadata==4.8.3
iniconfig==1.1.1
multidict==5.2.0
-e git+https://github.com/opsdroid/opsdroid.git@b67d32310da842dddddb9b907f731a03d0562a2f#egg=opsdroid
packaging==21.3
pluggy==1.0.0
py==1.11.0
pycron==0.40
pyparsing==3.1.4
pytest==7.0.1
pytest-cov==4.0.0
pytest-xdist==3.0.2
python-dateutil==2.9.0.post0
PyYAML==3.12
six==1.17.0
tomli==1.2.3
typing_extensions==4.1.1
yarl==0.10.3
zipp==3.6.0

name: opsdroid
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - aiohttp==2.1.0
      - arrow==0.10.0
      - async-timeout==4.0.2
      - attrs==22.2.0
      - chardet==5.0.0
      - coverage==6.2
      - execnet==1.9.0
      - idna==3.10
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - multidict==5.2.0
      - packaging==21.3
      - pluggy==1.0.0
      - py==1.11.0
      - pycron==0.40
      - pyparsing==3.1.4
      - pytest==7.0.1
      - pytest-cov==4.0.0
      - pytest-xdist==3.0.2
      - python-dateutil==2.9.0.post0
      - pyyaml==3.12
      - six==1.17.0
      - tomli==1.2.3
      - typing-extensions==4.1.1
      - yarl==0.10.3
      - zipp==3.6.0
prefix: /opt/conda/envs/opsdroid

tests/test_loader.py::TestLoader::test_load_config_file_with_env_vars
tests/test_loader.py::TestLoader::test_build_module_path tests/test_loader.py::TestLoader::test_check_cache_leaves tests/test_loader.py::TestLoader::test_check_cache_removes_dir tests/test_loader.py::TestLoader::test_check_cache_removes_file tests/test_loader.py::TestLoader::test_create_default_config tests/test_loader.py::TestLoader::test_generate_config_if_none_exist tests/test_loader.py::TestLoader::test_git_clone tests/test_loader.py::TestLoader::test_import_module tests/test_loader.py::TestLoader::test_import_module_failure tests/test_loader.py::TestLoader::test_import_module_new tests/test_loader.py::TestLoader::test_install_default_remote_module tests/test_loader.py::TestLoader::test_install_existing_module tests/test_loader.py::TestLoader::test_install_local_module_dir tests/test_loader.py::TestLoader::test_install_local_module_failure tests/test_loader.py::TestLoader::test_install_local_module_file tests/test_loader.py::TestLoader::test_install_missing_local_module tests/test_loader.py::TestLoader::test_install_specific_local_git_module tests/test_loader.py::TestLoader::test_install_specific_local_path_module tests/test_loader.py::TestLoader::test_install_specific_remote_module tests/test_loader.py::TestLoader::test_load_broken_config_file tests/test_loader.py::TestLoader::test_load_config tests/test_loader.py::TestLoader::test_load_config_file tests/test_loader.py::TestLoader::test_load_empty_config tests/test_loader.py::TestLoader::test_load_modules tests/test_loader.py::TestLoader::test_load_non_existant_config_file tests/test_loader.py::TestLoader::test_pip_install_deps tests/test_loader.py::TestLoader::test_reload_modules
docs/configuration-reference.md opsdroid/loader.py
docs/configuration-reference.md opsdroid/loader.py