{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-guides/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["admonition"]},"type":"markdown"},"seo":{"title":"Configuration Tutorial","description":"Accelerate E&P application development and protect your innovation by consuming our Data and Domain APIs / Platform APIs.","lang":"en-US","meta":[{"name":"robots","content":"noindex"}],"llmstxt":{"hide":true,"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"configuration-tutorial","__idx":0},"children":["Configuration Tutorial"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["By the end of the configuration tutorial you should feel comfortable using the SDK Configuration capabilities and have an understanding of the dynamic nature of configuration which is an integral part for making your application configurable and modifiable - even while the application is running."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Before you begin"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You should have completed the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/solutions/agora/quickstartguide"},"children":["Getting Started"]}," tutorial to integrate the Agora.Edge SDK into your project before starting and have a project ready to use with the tutorial."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"adding-aeajson-and-naming-your-app","__idx":1},"children":["Adding 'AEA.json' and Naming your App"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The default name for your application is the Entry Assembly Name.  This Name can be overridden using IIoT Edge App Configuration, which is very useful when configuring the application to interact with other applications."]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The Application Name (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Config[\"Name\"]"]},") is used as the NATS Client identifier when routing messages."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"_","__idx":2},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#tab/net"},"children":["NET"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Copy the following code into a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Program.cs"]}," file."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"csharp","header":{"controls":{"copy":{}}},"source":"using static Agora.SDK;\n\npublic class Program\n{\n    public static void Main(string[] args)\n    {\n        \"Starting\".LogHeading();\n        $\"Hello from {Config[\"Name\"]}!\".LogInfo();\n        \"Stopping\".LogHeading();\n    }\n}\n","lang":"csharp"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"_-1","__idx":3},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#tab/python"},"children":["Python"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Copy the following code into a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["program.py"]}," file."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"from agoraiot import config, logger\n\nlogger.heading(\"Starting\")\nlogger.info(f\"Hello from {config['Name']}!\")\nlogger.heading(\"Stopping\")\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Running the application will produce an output similar to:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"output","header":{"controls":{"copy":{}}},"source":"-------------------------------------------------------------------------------\n(33)                                                                   Starting\n-------------------------------------------------------------------------------\nI(36) - Hello from Module!\n-------------------------------------------------------------------------------\n(37)                                                                   Stopping\n-------------------------------------------------------------------------------\n","lang":"output"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To override the name of the application, add a json file to the project called ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["AEA.json"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To set the Name, open the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["AEA.json"]}," file and add the following:"]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["AEA.json"]}," configuration file:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n    \"Name\": \"MyApp\"\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"multiple-configuration-settings","__idx":4},"children":["Multiple Configuration Settings"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"part-i-nested-settings","__idx":5},"children":["Part I: Nested Settings"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["An ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["AEA.json"]}," file can contain nested settings."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Copy the contents below into your ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["AEA.json"]}," file of your application."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n    \"Name\": \"MyApp\",\n    \"MyApp\": {\n        \"Setting1\": \"Value\",\n        \"Setting2\": [\"An\", \"Array\", \"of\", \"Values\"]\n    }\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The following codes shows how to access the settings."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"_-2","__idx":6},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#tab/net"},"children":["NET"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Program.cs:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"csharp","header":{"controls":{"copy":{}}},"source":"using System.Text;\nusing static Agora.SDK;\n\npublic class Program\n{\n    public static void Main(string[] args)\n    {\n        Console.WriteLine($\"The name of my app is `{Config[\"Name\"]}'\");\n        Console.WriteLine($\"Setting1 = `{Config[\"MyApp:Setting1\"]}'\");\n\n        /* Accessing a setting with an array */\n        var settings = Config.GetSection(\"MyApp:Setting2\").GetChildren().AsEnumerable();\n\n        Console.Write(\"Setting2 contains `\");\n        foreach (var s in settings)\n            Console.Write(s.Value + ' ');\n\n        Console.WriteLine(\"'.\");\n    }\n}\n","lang":"csharp"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"_-3","__idx":7},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#tab/python"},"children":["Python"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["app.py:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"from agoraiot import logger, config\n\nprint(f\"The name of my app is '{config['Name']}'\")\nprint(f\"Setting 1 = '{config['MyApp:Setting1']}'\")\n\nsettings = config['MyApp:Setting2']\n\nprint(\"Setting 2 contains '\", end=\"\")\nfor setting in settings:\n    print(setting + ' ', end=\"\")\n\nprint(\"'.\")\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Output:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"output","header":{"controls":{"copy":{}}},"source":"The name of my app is `MyApp'\nSetting1 = `Value'\nSetting2 contains `An Array of Values '.\n","lang":"output"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"part-ii-using-redis-for-configuration","__idx":8},"children":["Part II: Using Redis for Configuration"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The purpose of the runtime configuration via Redis is to allow configuration of the application once it is deployed as a container.  Typically, the container would be installed on a Gateway, and AgoraOps would configure the application as needed for the problem being addressed by the application."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The SDK subscribes to a Redis key for runtime configuration. Updates to this key are read by the SDK at runtime and configuration is updated."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"key","header":{"controls":{"copy":{}}},"source":"twin_properties/config/desired/<app-name>\n","lang":"key"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When running as a container, the SDK loads the configuration components in the following order:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Default Settings: Set within the application."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The Primary Config: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["AEA.json"]}," of the application"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Runtime config updated in Redis key"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Override Settings: Set within the application."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To see how these work together, perform the following steps:"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"_-4","__idx":9},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#tab/net"},"children":["NET"]}]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Update the Redis key above with a new ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["AEA.json"]}," with the following contents and run the application"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n    \"MyApp\": {\n        \"Setting1\": \"New Value for Setting 1\"\n    }\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"_-5","__idx":10},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#tab/python"},"children":["Python"]}]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Using the same python code from the pervious tutorial go to the folder."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Update the Redis key above with a new ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["AEA.json"]}," with the following contents and run the application."]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n    \"MyApp\": {\n        \"Setting1\": \"New Value for Setting 1\"\n    }\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Output:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"output","header":{"controls":{"copy":{}}},"source":"The name of my app is `MyApp'\nSetting1 = `New Value for Setting 1'\nSetting2 contains `An Array of Values '.\n","lang":"output"},"children":[]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Settings (key names) are case insensitive."]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For Python SDK, the use of a switch map is required."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":4,"id":"part-iii-responding-to-configuration-changes","__idx":11},"children":["Part III: Responding To Configuration Changes"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["While your application runs at the edge, it is likely that you will want to make it respond to configuration changes.  You can do so by:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Using an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Agora.ObservableSetting"]},"."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Both methods are illustrated as below:"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["AEA.json:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"Name\": \"ConfigurationTutorial\",\n  \"Author\": \"Original Author\",\n  \"Authors\": [ \"Author1\", \"Author2\" ]\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"_-6","__idx":12},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#tab/net"},"children":["NET"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Program.cs:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"csharp","header":{"controls":{"copy":{}}},"source":"using static Agora.SDK;\n\npublic class Program\n{\n    static public void Main()\n    {\n        \"Starting\".LogHeading();\n\n        WriteAuthor();\n\n        // Method 1\n        //Config.GetReloadToken().RegisterChangeCallback(InvokeChange, null);\n        //\"If you modify any setting you will get a ReloadTokenChange\".Cout();\n\n        // Method 2\n        //ObservableSetting.Get(\"Author\").PropertyChanged += AuthorObservableSettingChanged;\n        //$\"If you modify the 'Author' setting, you will get an ObservableSetting notifications.\".Cout();\n\n        Console.ReadLine();\n\n        \"Stopping\".LogHeading();\n    }\n\n    private static void WriteAuthor() => $\"Author is set to `{Config[\"Author\"]}`\".Cout();\n\n    private static void AuthorObservableSettingChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)\n    {\n        WriteAuthor();\n    }\n\n    private static void InvokeChange(object o)\n    {\n        \"Configuration Reloaded:\".Cout();\n        WriteAuthor();\n\n        // You must re-register the change callback as the Configuration.ReloadToken has changed\n        Config.GetReloadToken().RegisterChangeCallback(InvokeChange, null);\n    }\n}\n","lang":"csharp"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"_-7","__idx":13},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#tab/python"},"children":["Python"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"from agoraiot import *\n\ndef handle_config_change():\n    print( \"Configuration Reloaded\" )\n    write_author()\n\ndef author_setting_change(payload):\n    write_author()\n\ndef write_author():\n    print( f\"Author is set to `{config[\"Author\"]}`\")\n\n\nlogger.heading(\"Starting\")\n\nwrite_author()\n\n# Method 1\n# config.observer_config(handle_config_change)\n# print(\"If you modify any setting you will get a ReloadTokenChange\")\n\n# Method 2\n# config.observe(\"Author\", author_setting_change)\n# print(\"If you modify the 'Author' setting, you will get a notification)\n\ninput(\"Waiting\")\n\nlogger.heading(\"Stopping\")\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Both methods of handling configuration change may be used, even at the same time.  If you uncomment the lines after each ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["// Method"]}," you can see what happens when you modify settings using the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["config/AEA.json"]}," file and the key-per-files such as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["config/keys/authors"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["config/keys/authors__0"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"printing-the-full-configuration","__idx":14},"children":["Printing the Full Configuration"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Sometimes it is necessary to understand the full configuration to assist in troubleshooting issues.  Below shows how to use the SDK to write the configuration to logger as Info."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"_-8","__idx":15},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#tab/python"},"children":["Python"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"logger.info( json.dumps(config.get_dict(), indent=4) )\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]}]},"headings":[{"value":"Configuration Tutorial","id":"configuration-tutorial","depth":2},{"value":"Adding 'AEA.json' and Naming your App","id":"adding-aeajson-and-naming-your-app","depth":3},{"value":"","id":"_","depth":1},{"value":"","id":"_-1","depth":1},{"value":"Multiple Configuration Settings","id":"multiple-configuration-settings","depth":3},{"value":"Part I: Nested Settings","id":"part-i-nested-settings","depth":4},{"value":"","id":"_-2","depth":1},{"value":"","id":"_-3","depth":1},{"value":"Part II: Using Redis for Configuration","id":"part-ii-using-redis-for-configuration","depth":4},{"value":"","id":"_-4","depth":1},{"value":"","id":"_-5","depth":1},{"value":"Part III: Responding To Configuration Changes","id":"part-iii-responding-to-configuration-changes","depth":4},{"value":"","id":"_-6","depth":1},{"value":"","id":"_-7","depth":1},{"value":"Printing the Full Configuration","id":"printing-the-full-configuration","depth":3},{"value":"","id":"_-8","depth":1}],"frontmatter":{"seo":{"title":"Configuration Tutorial"}},"lastModified":"2026-01-14T00:18:30.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/solutions/agora/tutorial/configurationtutorial","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}