Repository: splunk/addonfactory-ucc-generator (language: python)

Issue:
Introduce `ucc-gen init` command
It is hard for the beginners to start using UCC framework by the lack of the documentation and examples. That is what I will be working on for some time in the future.

But for now, we can also introduce a `ucc-gen init` command that creates a very minimal version of the add-on that can be loaded to Splunk and should be working without any additional modifications.

This command should accept the following arguments: TBD

Commands to execute:

```
python3 -m venv .venv
source .venv/bin/activate
pip install splunk-add-on-ucc-framework
pip install splunk-packaging-toolkit
ucc-gen init <params to be defined>
ucc-gen --ta-version=0.0.1
slim package output/<addon_name>
```


Discussion / hints:
This will be released as part of `v5.19.0` release.

Fix patch:
diff --git a/.licenserc.yaml b/.licenserc.yaml
index 8c53bbbbc..c31287fa5 100644
--- a/.licenserc.yaml
+++ b/.licenserc.yaml
@@ -35,3 +35,4 @@ header:
     - "mkdocs.yml"
     - ".*"
     - "example/**"
+    - "splunk_add_on_ucc_framework/commands/init_template/**"
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 5007eda11..06c8473f9 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -44,3 +44,4 @@ repos:
     rev: 4.0.1
     hooks:
     -   id: flake8
+        exclude: ^splunk_add_on_ucc_framework/commands/init_template/
diff --git a/docs/additional_packaging.md b/docs/additional_packaging.md
new file mode 100644
index 000000000..f0b0c61c6
--- /dev/null
+++ b/docs/additional_packaging.md
@@ -0,0 +1,10 @@
+# `additional_packaging.py` file
+
+To extend the build process, you can create `additional_packaging.py` file in the same file level where you have your globalConfig file.
+
+This file should have `additional_packaging` function which accepts 1 argument: add-on name.
+
+Example of how to utilize it:
+
+* Build custom UI after `ucc-gen` finishes all its necessary steps.
+* Workaround a `ucc-gen` feature which was not implemented.
diff --git a/docs/index.md b/docs/index.md
index 1ce42729d..77e9f3910 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -13,6 +13,9 @@ action templates.
 Available as a GitHub action here:
 <https://github.com/splunk/addonfactory-ucc-generator-action>
 
+You can use [Splunk Extension for VSCode](https://marketplace.visualstudio.com/items?itemName=Splunk.splunk) 
+as well.
+
 ## What is UCC?
 
 UCC stands for Universal Configuration Console. The purpose of having a
diff --git a/docs/how_to_use.md b/docs/quickstart.md
similarity index 60%
rename from docs/how_to_use.md
rename to docs/quickstart.md
index 0cc0f2e9b..77ad82a19 100644
--- a/docs/how_to_use.md
+++ b/docs/quickstart.md
@@ -1,4 +1,4 @@
-# How To Use
+# Quickstart
 
 ## Prerequisites
 
@@ -14,13 +14,7 @@ To be able to create an add-on using UCC framework, you need to have at least:
 
 > If both globalConfig.json and globalConfig.yaml files are present, then the globalConfig.json file will take precedence.
 
-An example of creating a basic add-on can be found [here](example.md).
-
-### VSCode extension
-
-You can use [Splunk Extension for VSCode](https://marketplace.visualstudio.com/items?itemName=Splunk.splunk) as well.
-
-### JSON schema for globalConfig
+An example of creating a basic add-on from scratch can be found [here](example.md).
 
 The JSON schema for the `globalConfig` file can be found
 [here](https://github.com/splunk/addonfactory-ucc-base-ui/blob/main/src/main/webapp/schema/schema.json).
@@ -31,7 +25,9 @@ Refer to Splunkbase documentation
 [here](https://dev.splunk.com/enterprise/docs/releaseapps/splunkbase/namingguidelines/) 
 to figure out what should be the name of your add-on.
 
-## Steps to generate the add-on
+## Initialize new add-on
+
+> Initialization of the new add-on is available from `5.19.0` version of `ucc-gen`.
 
 Commands below are macOS and Linux specific.
 
@@ -42,7 +38,33 @@ python3 -m venv .venv
 source .venv/bin/activate
 ```
 
-* Install `splunk-add-on-ucc-framework` and `splunk-packaging-toolkit`
+* Install `splunk-add-on-ucc-framework`
+
+```bash
+pip install splunk-add-on-ucc-framework
+```
+
+* Initialize new add-on
+
+```bash
+ucc-gen init --addon-name "demo_addon_for_splunk" --addon-display-name "Demo Add-on for Splunk" --addon-input-name demo_input
+```
+
+The new add-on is located in `demo_addon_for_splunk` folder and can be built using 
+the commands [below](#steps-to-build-already-existing-add-on).
+
+## Build already existing add-on
+
+Commands below are macOS and Linux specific.
+
+* Set up and activate Python virtual environment (skip if you already have an environment)
+
+```bash
+python3 -m venv .venv
+source .venv/bin/activate
+```
+
+* Install `splunk-add-on-ucc-framework` and `splunk-packaging-toolkit` (skip if you already installed libraries)
 
 ```bash
 pip install splunk-add-on-ucc-framework splunk-packaging-toolkit
@@ -57,6 +79,8 @@ ucc-gen
 slim package output/<add-on-name>
 ```
 
+> Please use `ucc-gen build` instead of `ucc-gen` if you are using UCC `v5.19.0` and higher.
+
 Now you should see an archive created in the same level as your 
 `globalConfig.json` is located.
 
@@ -70,29 +94,42 @@ folder and extending their functionality. The generated inputs are using
 update the modular input code, you can run `ucc-gen` again and `ucc-gen` will 
 use updated modular inputs from `package/bin` instead of generating new ones.
 
-## Subcommands
+## Commands
 
 As of now, running `ucc-gen` does the same thing as running `ucc-gen build`, 
 but eventually calling `ucc-gen` without specifying a subcommand will be 
 deprecated. 
 
-* `build` - [optional] used to build the add-on 
+* `build` - build the add-on.
 
-    * `source` - [optional] folder containing the `app.manifest` and app 
+    * `--source` - [optional] folder containing the `app.manifest` and app 
         source.
-    * `config` - [optional] path to the configuration file, defaults to
+    * `--config` - [optional] path to the configuration file, defaults to
         globalConfig file in the parent directory of source provided.
-    * `ta-version` - [optional] override current version of TA, default
+    * `--ta-version` - [optional] override current version of TA, default
         version is version specified in `globalConfig.json` or `globalConfig.yaml`. 
         Splunkbase compatible version of SEMVER will be used by default.
-    * `python-binary-name` - [optional] Python binary name to use when
+    * `--python-binary-name` - [optional] Python binary name to use when
         installing Python libraries.
 
-## Parameters
+* `init` - initialize the add-on (available from `v5.19.0`).
 
-`ucc-gen` supports the following params:
+    * `--addon-name` - [required] add-on name. Consult with 
+        [official naming convention guide](https://dev.splunk.com/enterprise/docs/releaseapps/splunkbase/namingguidelines/).
+    * `--addon-display-name` - [required] add-on "official" name.
+    * `--addon-input-name` - [required] name of the generated input. 
+    * `--addon-version` - [optional] version of the generated add-on, `0.0.1` by default.
+    * `--overwrite` - [optional] overwrites already existing folder if used, 
+        by default you can't generate a new add-on to already existing folder.
 
+`ucc-gen init` command initializes the add-on and bootstraps some code in the 
+modular input which you, as a developer, can extend for your needs.
 
+Apart from standard files needed for the add-on, it also adds search head 
+clustering files in `default/server.conf` file and reload triggers in 
+`default/app.conf` file. Those files will be generated automatically soon by 
+`ucc-gen build` command itself, for now you need to include them manually 
+during the add-on development.
 
 ## What `ucc-gen` does
 
@@ -114,14 +151,3 @@ deprecated.
 * Copies addon's `package/*` to `output/<package_ID>/*` directory.
 * If an addon requires some additional configurations in packaging
     then `ucc-gen` runs the code in the `additional_packaging.py` file as well.
-
-## `additional_packaging.py` file
-
-To extend the build process, you can create `additional_packaging.py` file in the same file level where you have your globalConfig file.
-
-This file should have `additional_packaging` function which accepts 1 argument: add-on name.
-
-Example of how to utilize it:
-
-* Build custom UI after `ucc-gen` finishes all its necessary steps.
-* Workaround a `ucc-gen` feature which was not implemented.
diff --git a/example/globalConfig.json b/example/globalConfig.json
index 83658fd8e..a4a327646 100644
--- a/example/globalConfig.json
+++ b/example/globalConfig.json
@@ -161,8 +161,8 @@
     "meta": {
         "name": "Splunk_TA_dummy_data",
         "restRoot": "Splunk_TA_dummy_data",
-        "version": "5.18.0R9222a5a8",
+        "version": "5.18.0R05a4667a",
         "displayName": "Splunk_TA_dummy_data",
         "schemaVersion": "0.0.3"
     }
-}
\ No newline at end of file
+}
diff --git a/example/package/bin/dummy_input.py b/example/package/bin/dummy_input.py
index f81fdf262..b40a1ac43 100644
--- a/example/package/bin/dummy_input.py
+++ b/example/package/bin/dummy_input.py
@@ -45,7 +45,7 @@ def stream_events(self, inputs, ew):
                 {"dummy": "data"},
             ]
             event = smi.Event(
-                data=json.dumps(dummy_data_to_ingest),
+                data=json.dumps(dummy_data_to_ingest, ensure_ascii=False, default=str),
                 sourcetype='dummy_input',
             )
             ew.write_event(event)
diff --git a/mkdocs.yml b/mkdocs.yml
index e7ad3268d..a6f14bd75 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -1,4 +1,4 @@
-site_name: Splunk Add-on UCC Framework
+site_name: UCC Framework
 site_author: Splunk
 site_url: "https://splunk.github.io/addonfactory-ucc-generator/"
 edit_uri: "tree/main/docs/"
@@ -23,10 +23,11 @@ theme:
 
 nav:
   - Home: "index.md"
-  - How to use: "how_to_use.md"
+  - Quickstart: "quickstart.md"
   - Example: "example.md"
   - Components: "components.md"
   - Tabs: "tabs.md"
+  - Additional packaging: "additional_packaging.md"
   - Custom UI Extensions:
     - Custom Hook: "custom_ui_extensions/custom_hook.md"
     - Custom Control: "custom_ui_extensions/custom_control.md"
diff --git a/poetry.lock b/poetry.lock
index ceb2fd89f..b42574f4a 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -6,6 +6,18 @@ category = "main"
 optional = false
 python-versions = ">=3.7,<4.0"
 
+[[package]]
+name = "arrow"
+version = "1.2.3"
+description = "Better dates & times for Python"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+python-dateutil = ">=2.7.0"
+typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
+
 [[package]]
 name = "atomicwrites"
 version = "1.4.1"
@@ -16,31 +28,52 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
 
 [[package]]
 name = "attrs"
-version = "22.1.0"
+version = "22.2.0"
 description = "Classes Without Boilerplate"
 category = "main"
 optional = false
-python-versions = ">=3.5"
+python-versions = ">=3.6"
 
 [package.extras]
-dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"]
-docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"]
-tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"]
-tests_no_zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"]
+cov = ["attrs", "coverage-enable-subprocess", "coverage[toml] (>=5.3)"]
+dev = ["attrs"]
+docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope.interface"]
+tests = ["attrs", "zope.interface"]
+tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=0.971,<0.990)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist"]
+tests_no_zope = ["cloudpickle", "hypothesis", "mypy (>=0.971,<0.990)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist"]
+
+[[package]]
+name = "binaryornot"
+version = "0.4.4"
+description = "Ultra-lightweight pure Python package to check if a file is binary or text."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+chardet = ">=3.0.2"
 
 [[package]]
 name = "certifi"
 version = "2022.12.7"
 description = "Python package for providing Mozilla's CA Bundle."
-category = "dev"
+category = "main"
 optional = false
 python-versions = ">=3.6"
 
+[[package]]
+name = "chardet"
+version = "5.1.0"
+description = "Universal encoding detector for Python 3"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
 [[package]]
 name = "charset-normalizer"
 version = "2.1.1"
 description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
-category = "dev"
+category = "main"
 optional = false
 python-versions = ">=3.6.0"
 
@@ -51,7 +84,7 @@ unicode_backport = ["unicodedata2"]
 name = "click"
 version = "8.1.3"
 description = "Composable command line interface toolkit"
-category = "dev"
+category = "main"
 optional = false
 python-versions = ">=3.7"
 
@@ -63,10 +96,27 @@ importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
 name = "colorama"
 version = "0.4.6"
 description = "Cross-platform colored terminal text."
-category = "dev"
+category = "main"
 optional = false
 python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
 
+[[package]]
+name = "cookiecutter"
+version = "2.1.1"
+description = "A command-line utility that creates projects from project templates, e.g. creating a Python package project from a Python package project template."
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+binaryornot = ">=0.4.4"
+click = ">=7.0,<9.0.0"
+Jinja2 = ">=2.7,<4.0.0"
+jinja2-time = ">=0.2.0"
+python-slugify = ">=4.0.0"
+pyyaml = ">=5.3.1"
+requests = ">=2.23.0"
+
 [[package]]
 name = "cssselect"
 version = "1.2.0"
@@ -178,7 +228,7 @@ pyparsing = {version = ">=2.4.2,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.0.2 || >3.0
 name = "idna"
 version = "3.4"
 description = "Internationalized Domain Names in Applications (IDNA)"
-category = "dev"
+category = "main"
 optional = false
 python-versions = ">=3.5"
 
@@ -236,6 +286,18 @@ MarkupSafe = ">=2.0"
 [package.extras]
 i18n = ["Babel (>=2.7)"]
 
+[[package]]
+name = "jinja2-time"
+version = "0.2.0"
+description = "Jinja2 Extension for Dates and Times"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+arrow = "*"
+jinja2 = "*"
+
 [[package]]
 name = "jsonschema"
 version = "4.17.3"
@@ -576,7 +638,7 @@ testing = ["filelock"]
 name = "python-dateutil"
 version = "2.8.2"
 description = "Extensions to the standard Python datetime module"
-category = "dev"
+category = "main"
 optional = false
 python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
 
@@ -594,6 +656,20 @@ python-versions = ">=3.7"
 [package.extras]
 cli = ["click (>=5.0)"]
 
+[[package]]
+name = "python-slugify"
+version = "7.0.0"
+description = "A Python slugify application that also handles Unicode"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+text-unidecode = ">=1.3"
+
+[package.extras]
+unidecode = ["Unidecode (>=1.1.1)"]
+
 [[package]]
 name = "pyyaml"
 version = "6.0"
@@ -617,7 +693,7 @@ pyyaml = "*"
 name = "requests"
 version = "2.28.1"
 description = "Python HTTP for Humans."
-category = "dev"
+category = "main"
 optional = false
 python-versions = ">=3.7, <4"
 
@@ -646,7 +722,7 @@ urllib3 = "*"
 name = "six"
 version = "1.16.0"
 description = "Python 2 and 3 compatibility utilities"
-category = "dev"
+category = "main"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
 
@@ -658,6 +734,14 @@ category = "dev"
 optional = false
 python-versions = "*"
 
+[[package]]
+name = "text-unidecode"
+version = "1.3"
+description = "The most basic Text::Unidecode port"
+category = "main"
+optional = false
+python-versions = "*"
+
 [[package]]
 name = "toml"
 version = "0.10.2"
@@ -695,7 +779,7 @@ python-versions = ">=3.7"
 name = "urllib3"
 version = "1.26.13"
 description = "HTTP library with thread-safe connection pooling, file post, and more."
-category = "dev"
+category = "main"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
 
@@ -768,24 +852,36 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools"
 [metadata]
 lock-version = "1.1"
 python-versions = "^3.7"
-content-hash = "61ae81b34cdc424949debdaa2a6d78e631bac4260834c693d587ca2d7eb6e2fa"
+content-hash = "dcc26aa8f9880cf01072efad78ec2ec6717ac67957d598d4828083700b1d0666"
 
 [metadata.files]
 addonfactory-splunk-conf-parser-lib = [
     {file = "addonfactory_splunk_conf_parser_lib-0.3.4-py3-none-any.whl", hash = "sha256:f2eb25bd03d4fd464b8b0304a87d5dc1e2935c42a6c262c6461b9b73b8e30217"},
     {file = "addonfactory_splunk_conf_parser_lib-0.3.4.tar.gz", hash = "sha256:e550105c8c85a6f553688e5b1f4f55ae7ca758a1a428cbce1e0b9b73b9f4e35d"},
 ]
+arrow = [
+    {file = "arrow-1.2.3-py3-none-any.whl", hash = "sha256:5a49ab92e3b7b71d96cd6bfcc4df14efefc9dfa96ea19045815914a6ab6b1fe2"},
+    {file = "arrow-1.2.3.tar.gz", hash = "sha256:3934b30ca1b9f292376d9db15b19446088d12ec58629bc3f0da28fd55fb633a1"},
+]
 atomicwrites = [
     {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"},
 ]
 attrs = [
-    {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"},
-    {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"},
+    {file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"},
+    {file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"},
+]
+binaryornot = [
+    {file = "binaryornot-0.4.4-py2.py3-none-any.whl", hash = "sha256:b8b71173c917bddcd2c16070412e369c3ed7f0528926f70cac18a6c97fd563e4"},
+    {file = "binaryornot-0.4.4.tar.gz", hash = "sha256:359501dfc9d40632edc9fac890e19542db1a287bbcfa58175b66658392018061"},
 ]
 certifi = [
     {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"},
     {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"},
 ]
+chardet = [
+    {file = "chardet-5.1.0-py3-none-any.whl", hash = "sha256:362777fb014af596ad31334fde1e8c327dfdb076e1960d1694662d46a6917ab9"},
+    {file = "chardet-5.1.0.tar.gz", hash = "sha256:0d62712b956bc154f85fb0a266e2a3c5913c2967e00348701b32411d6def31e5"},
+]
 charset-normalizer = [
     {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"},
     {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"},
@@ -798,6 +894,10 @@ colorama = [
     {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
     {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
 ]
+cookiecutter = [
+    {file = "cookiecutter-2.1.1-py2.py3-none-any.whl", hash = "sha256:9f3ab027cec4f70916e28f03470bdb41e637a3ad354b4d65c765d93aad160022"},
+    {file = "cookiecutter-2.1.1.tar.gz", hash = "sha256:f3982be8d9c53dac1261864013fdec7f83afd2e42ede6f6dd069c5e149c540d5"},
+]
 cssselect = [
     {file = "cssselect-1.2.0-py2.py3-none-any.whl", hash = "sha256:da1885f0c10b60c03ed5eccbb6b68d6eff248d91976fcde348f395d54c9fd35e"},
     {file = "cssselect-1.2.0.tar.gz", hash = "sha256:666b19839cfaddb9ce9d36bfe4c969132c647b92fc9088c4e23f786b30f1b3dc"},
@@ -857,6 +957,10 @@ jinja2 = [
     {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"},
     {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"},
 ]
+jinja2-time = [
+    {file = "jinja2-time-0.2.0.tar.gz", hash = "sha256:d14eaa4d315e7688daa4969f616f226614350c48730bfa1692d2caebd8c90d40"},
+    {file = "jinja2_time-0.2.0-py2.py3-none-any.whl", hash = "sha256:d3eab6605e3ec8b7a0863df09cc1d23714908fa61aa6986a845c20ba488b4efa"},
+]
 jsonschema = [
     {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"},
     {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"},
@@ -1097,6 +1201,10 @@ python-dotenv = [
     {file = "python-dotenv-0.21.0.tar.gz", hash = "sha256:b77d08274639e3d34145dfa6c7008e66df0f04b7be7a75fd0d5292c191d79045"},
     {file = "python_dotenv-0.21.0-py3-none-any.whl", hash = "sha256:1684eb44636dd462b66c3ee016599815514527ad99965de77f43e0944634a7e5"},
 ]
+python-slugify = [
+    {file = "python-slugify-7.0.0.tar.gz", hash = "sha256:7a0f21a39fa6c1c4bf2e5984c9b9ae944483fd10b54804cb0e23a3ccd4954f0b"},
+    {file = "python_slugify-7.0.0-py2.py3-none-any.whl", hash = "sha256:003aee64f9fd955d111549f96c4b58a3f40b9319383c70fad6277a4974bbf570"},
+]
 pyyaml = [
     {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"},
     {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"},
@@ -1151,6 +1259,10 @@ six = [
 splunk-sdk = [
     {file = "splunk-sdk-1.7.2.tar.gz", hash = "sha256:36d27f83e7cc8309be6e3b92e46c547caea566b0c006238025073843e586e5a8"},
 ]
+text-unidecode = [
+    {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"},
+    {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"},
+]
 toml = [
     {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
     {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
diff --git a/pyproject.toml b/pyproject.toml
index bea83377a..3cdd46e61 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -41,6 +41,7 @@ addonfactory-splunk-conf-parser-lib = "^0.3.3"
 dunamai = "^1.9.0"
 jsonschema = "^4.4.0"
 PyYAML = "^6.0"
+cookiecutter = "^2.1.1"
 
 [tool.poetry.dev-dependencies]
 pytest = "^6.2"
diff --git a/splunk_add_on_ucc_framework/commands/init.py b/splunk_add_on_ucc_framework/commands/init.py
new file mode 100644
index 000000000..9e86ece92
--- /dev/null
+++ b/splunk_add_on_ucc_framework/commands/init.py
@@ -0,0 +1,55 @@
+#
+# Copyright 2021 Splunk Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import logging
+import os
+
+from cookiecutter import exceptions, main
+
+logger = logging.getLogger("ucc_gen")
+
+
+def init(
+    addon_name: str,
+    addon_display_name: str,
+    addon_input_name: str,
+    addon_version: str,
+    overwrite: bool = False,
+):
+    try:
+        generated_addon_path = main.cookiecutter(
+            template=os.path.join(
+                os.path.dirname(__file__),
+                "init_template",
+            ),
+            overwrite_if_exists=overwrite,
+            no_input=True,
+            extra_context={
+                "addon_name": addon_name,
+                "addon_display_name": addon_display_name,
+                "addon_input_name": addon_input_name,
+                "addon_version": addon_version,
+            },
+        )
+        logger.info(f"Generated add-on is located here {generated_addon_path}")
+        logger.info(
+            "LICENSE.txt and README.txt are empty, "
+            "you may need to modify the content of those files. "
+        )
+    except exceptions.OutputDirExistsException:
+        logger.error(
+            "The location is already taken, use `--overwrite` "
+            "option to overwrite the content of existing folder."
+        )
diff --git a/splunk_add_on_ucc_framework/commands/init_template/cookiecutter.json b/splunk_add_on_ucc_framework/commands/init_template/cookiecutter.json
new file mode 100644
index 000000000..815474084
--- /dev/null
+++ b/splunk_add_on_ucc_framework/commands/init_template/cookiecutter.json
@@ -0,0 +1,6 @@
+{
+    "addon_name": "",
+    "addon_display_name": "",
+    "addon_input_name": "",
+    "addon_version": ""
+}
\ No newline at end of file
diff --git a/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/README.md b/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/README.md
new file mode 100644
index 000000000..8726f0c39
--- /dev/null
+++ b/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/README.md
@@ -0,0 +1,1 @@
+# {{cookiecutter.addon_name}}
diff --git a/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/globalConfig.json b/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/globalConfig.json
new file mode 100644
index 000000000..ef7477d24
--- /dev/null
+++ b/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/globalConfig.json
@@ -0,0 +1,206 @@
+{
+    "pages": {
+        "configuration": {
+            "tabs": [
+                {
+                    "name": "account",
+                    "table": {
+                        "actions": [
+                            "edit",
+                            "delete",
+                            "clone"
+                        ],
+                        "header": [
+                            {
+                                "label": "Name",
+                                "field": "name"
+                            }
+                        ]
+                    },
+                    "entity": [
+                        {
+                            "type": "text",
+                            "label": "Name",
+                            "validators": [
+                                {
+                                    "type": "regex",
+                                    "errorMsg": "Input Name must begin with a letter and consist exclusively of alphanumeric characters and underscores.",
+                                    "pattern": "^[a-zA-Z]\\w*$"
+                                },
+                                {
+                                    "type": "string",
+                                    "errorMsg": "Length of input name should be between 1 and 100",
+                                    "minLength": 1,
+                                    "maxLength": 100
+                                }
+                            ],
+                            "field": "name",
+                            "help": "A unique name for the data input.",
+                            "required": true
+                        },
+                        {
+                            "type": "text",
+                            "label": "API key",
+                            "field": "api_key",
+                            "help": "API key",
+                            "required": true,
+                            "encrypted": true
+                        }
+                    ],
+                    "title": "Accounts"
+                },
+                {
+                    "name": "logging",
+                    "entity": [
+                        {
+                            "type": "singleSelect",
+                            "label": "Log level",
+                            "options": {
+                                "disableSearch": true,
+                                "autoCompleteFields": [
+                                    {
+                                        "value": "DEBUG",
+                                        "label": "DEBUG"
+                                    },
+                                    {
+                                        "value": "INFO",
+                                        "label": "INFO"
+                                    },
+                                    {
+                                        "value": "WARN",
+                                        "label": "WARN"
+                                    },
+                                    {
+                                        "value": "ERROR",
+                                        "label": "ERROR"
+                                    },
+                                    {
+                                        "value": "CRITICAL",
+                                        "label": "CRITICAL"
+                                    }
+                                ]
+                            },
+                            "defaultValue": "INFO",
+                            "field": "loglevel"
+                        }
+                    ],
+                    "title": "Logging"
+                }
+            ],
+            "title": "Configuration",
+            "description": "Set up your add-on"
+        },
+        "inputs": {
+            "services": [
+                {
+                    "name": "{{cookiecutter.addon_input_name}}",
+                    "entity": [
+                        {
+                            "type": "text",
+                            "label": "Name",
+                            "validators": [
+                                {
+                                    "type": "regex",
+                                    "errorMsg": "Input Name must begin with a letter and consist exclusively of alphanumeric characters and underscores.",
+                                    "pattern": "^[a-zA-Z]\\w*$"
+                                },
+                                {
+                                    "type": "string",
+                                    "errorMsg": "Length of input name should be between 1 and 100",
+                                    "minLength": 1,
+                                    "maxLength": 100
+                                }
+                            ],
+                            "field": "name",
+                            "help": "A unique name for the data input.",
+                            "required": true
+                        },
+                        {
+                            "type": "text",
+                            "label": "Interval",
+                            "validators": [
+                                {
+                                    "type": "regex",
+                                    "errorMsg": "Interval must be an integer.",
+                                    "pattern": "^\\-[1-9]\\d*$|^\\d*$"
+                                }
+                            ],
+                            "defaultValue": "300",
+                            "field": "interval",
+                            "help": "Time interval of the data input, in seconds.",
+                            "required": true
+                        },
+                        {
+                            "type": "singleSelect",
+                            "label": "Account to use",
+                            "options": {
+                                "referenceName": "account"
+                            },
+                            "help": "Account to use for this input.",
+                            "field": "account",
+                            "required": true
+                        }
+                    ],
+                    "title": "{{cookiecutter.addon_input_name}}"
+                }
+            ],
+            "title": "Inputs",
+            "description": "Manage your data inputs",
+            "table": {
+                "actions": [
+                    "edit",
+                    "enable",
+                    "delete",
+                    "clone"
+                ],
+                "header": [
+                    {
+                        "label": "Name",
+                        "field": "name"
+                    },
+                    {
+                        "label": "Interval",
+                        "field": "interval"
+                    },
+                    {
+                        "label": "Index",
+                        "field": "index"
+                    },
+                    {
+                        "label": "Status",
+                        "field": "disabled"
+                    }
+                ],
+                "moreInfo": [
+                    {
+                        "label": "Name",
+                        "field": "name"
+                    },
+                    {
+                        "label": "Interval",
+                        "field": "interval"
+                    },
+                    {
+                        "label": "Index",
+                        "field": "index"
+                    },
+                    {
+                        "label": "Status",
+                        "field": "disabled",
+                        "mapping": {
+                            "true": "Disabled",
+                            "false": "Enabled"
+                        }
+                    }
+                ]
+            }
+        }
+    },
+    "meta": {
+        "name": "{{cookiecutter.addon_name}}",
+        "restRoot": "{{cookiecutter.addon_name}}",
+        "version": "{{cookiecutter.addon_version}}",
+        "displayName": "{{cookiecutter.addon_display_name}}",
+        "schemaVersion": "0.0.3"
+    }
+}
\ No newline at end of file
diff --git a/tests/testdata/test_addons/package_no_global_config/metadata/.gitkeep b/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/LICENSE.txt
similarity index 100%
rename from tests/testdata/test_addons/package_no_global_config/metadata/.gitkeep
rename to splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/LICENSE.txt
diff --git a/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/README.txt b/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/README.txt
new file mode 100644
index 000000000..e69de29bb
diff --git a/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/app.manifest b/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/app.manifest
new file mode 100644
index 000000000..4e8435cca
--- /dev/null
+++ b/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/app.manifest
@@ -0,0 +1,57 @@
+{
+    "schemaVersion": "2.0.0",
+    "info": {
+        "title": "{{cookiecutter.addon_display_name}}",
+        "id": {
+            "group": null,
+            "name": "{{cookiecutter.addon_name}}",
+            "version": "{{cookiecutter.addon_version}}"
+        },
+        "author": [
+            {
+                "name": "",
+                "email": null,
+                "company": null
+            }
+        ],
+        "releaseDate": null,
+        "description": "{{cookiecutter.addon_display_name}}",
+        "classification": {
+            "intendedAudience": "IT Professionals",
+            "categories": [
+                "Security, Fraud & Compliance"
+            ],
+            "developmentStatus": "Production/Stable"
+        },
+        "commonInformationModels": null,
+        "license": {
+            "name": null,
+            "text": "LICENSE.txt",
+            "uri": null
+        },
+        "privacyPolicy": {
+            "name": null,
+            "text": null,
+            "uri": null
+        },
+        "releaseNotes": {
+            "name": "README",
+            "text": "README.txt",
+            "uri": ""
+        }
+    },
+    "dependencies": null,
+    "tasks": null,
+    "inputGroups": null,
+    "incompatibleApps": null,
+    "platformRequirements": null,
+    "supportedDeployments": [
+        "_standalone",
+        "_distributed",
+        "_search_head_clustering"
+    ],
+    "targetWorkloads": [
+        "_search_heads",
+        "_indexers"
+    ]
+}
\ No newline at end of file
diff --git a/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/bin/{{cookiecutter.addon_input_name}}.py b/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/bin/{{cookiecutter.addon_input_name}}.py
new file mode 100644
index 000000000..b46f11106
--- /dev/null
+++ b/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/bin/{{cookiecutter.addon_input_name}}.py
@@ -0,0 +1,108 @@
+import json
+import logging
+import sys
+import traceback
+
+import import_declare_test
+from solnlib import conf_manager, log
+from splunklib import modularinput as smi
+
+ADDON_NAME = "{{cookiecutter.addon_name}}"
+
+
+def logger_for_input(input_name: str) -> logging.Logger:
+    normalized_input = input_name.split("/")[-1]
+    return log.Logs().get_logger(
+        f"{{cookiecutter.addon_name|lower}}_{normalized_input}"
+    )
+
+
+def get_account_api_key(session_key: str, account_name: str):
+    cfm = conf_manager.ConfManager(
+        session_key,
+        ADDON_NAME,
+        realm=f"__REST_CREDENTIAL__#{ADDON_NAME}#configs/conf-{{cookiecutter.addon_name}}_account",
+    )
+    account_conf_file = cfm.get_conf("{{cookiecutter.addon_name}}_account")
+    return account_conf_file.get(account_name).get("username")
+
+
+def get_data_from_api(logger: logging.Logger, api_key: str):
+    logger.info("Getting data from an external API")
+    dummy_data = [
+        {
+            "line1": "hello",
+        },
+        {
+            "line2": "world",
+        },
+    ]
+    return dummy_data
+
+
+class Input(smi.Script):
+    def __init__(self):
+        super().__init__()
+
+    def get_scheme(self):
+        scheme = smi.Scheme("{{cookiecutter.addon_input_name}}")
+        scheme.description = "{{cookiecutter.addon_input_name}} input"
+        scheme.use_external_validation = True
+        scheme.streaming_mode_xml = True
+        scheme.use_single_instance = False
+        scheme.add_argument(
+            smi.Argument(
+                "name", title="Name", description="Name", required_on_create=True
+            )
+        )
+        return scheme
+
+    def validate_input(self, definition: smi.ValidationDefinition):
+        return
+
+    def stream_events(self, inputs: smi.InputDefinition, event_writer: smi.EventWriter):
+        # inputs.inputs is a Python dictionary object like:
+        # {
+        #   "{{cookiecutter.addon_input_name}}://<input_name>": {
+        #     "account": "<account_name>",
+        #     "disabled": "0",
+        #     "host": "$decideOnStartup",
+        #     "index": "<index_name>",
+        #     "interval": "<interval_value>",
+        #     "python.version": "python3",
+        #   },
+        # }
+        for input_name, input_item in inputs.inputs.items():
+            logger = logger_for_input(input_name)
+            try:
+                session_key = self._input_definition.metadata["session_key"]
+                log_level = conf_manager.get_log_level(
+                    logger=logger,
+                    session_key=session_key,
+                    app_name=ADDON_NAME,
+                    conf_name=f"{ADDON_NAME}_settings",
+                )
+                logger.setLevel(log_level)
+                logger.info("Start of the modular input")
+                api_key = get_account_api_key(session_key, input_item.get("account"))
+                data = get_data_from_api(logger, api_key)
+                for line in data:
+                    event_writer.write_event(
+                        smi.Event(
+                            data=json.dumps(line, ensure_ascii=False, default=str),
+                            index=input_item.get("index"),
+                            sourcetype="dummy-data",
+                        )
+                    )
+                logger.info("End of the modular input")
+            except Exception as e:
+                logger.error(
+                    f"Exception raised while ingesting data for "
+                    f"{{cookiecutter.addon_input_name}}: {e}. Traceback: "
+                    f"{traceback.format_exc()}"
+                )
+
+
+if __name__ == "__main__":
+    exit_code = Input().run(sys.argv)
+    sys.exit(exit_code)
diff --git a/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/default/app.conf b/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/default/app.conf
new file mode 100644
index 000000000..3b554e0ea
--- /dev/null
+++ b/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/default/app.conf
@@ -0,0 +1,23 @@
+[install]
+is_configured = false
+state = enabled
+build = 1
+
+[launcher]
+description = {{cookiecutter.addon_display_name}}
+version = {{cookiecutter.addon_version}}
+
+[ui]
+is_visible = true
+label = {{cookiecutter.addon_display_name}}
+
+[package]
+id = {{cookiecutter.addon_name}}
+
+[id]
+name = {{cookiecutter.addon_name}}
+version = {{cookiecutter.addon_version}}
+
+[triggers]
+reload.{{cookiecutter.addon_name}}_settings = simple
+reload.{{cookiecutter.addon_name}}_account = simple
diff --git a/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/default/server.conf b/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/default/server.conf
new file mode 100644
index 000000000..cfcc16cc0
--- /dev/null
+++ b/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/default/server.conf
@@ -0,0 +1,3 @@
+[shclustering]
+conf_replication_include.{{cookiecutter.addon_name}}_settings = true
+conf_replication_include.{{cookiecutter.addon_name}}_account = true
diff --git a/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/default/{{cookiecutter.addon_name}}_settings.conf b/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/default/{{cookiecutter.addon_name}}_settings.conf
new file mode 100644
index 000000000..f2566d453
--- /dev/null
+++ b/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/default/{{cookiecutter.addon_name}}_settings.conf
@@ -0,0 +1,2 @@
+[logging]
+loglevel = INFO
diff --git a/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/lib/requirements.txt b/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/lib/requirements.txt
new file mode 100644
index 000000000..99f4b184d
--- /dev/null
+++ b/splunk_add_on_ucc_framework/commands/init_template/{{cookiecutter.addon_name}}/package/lib/requirements.txt
@@ -0,0 +1,3 @@
+splunktaucclib
+splunk-sdk
+solnlib
diff --git a/splunk_add_on_ucc_framework/main.py b/splunk_add_on_ucc_framework/main.py
index 1c3d9a15c..96f579428 100644
--- a/splunk_add_on_ucc_framework/main.py
+++ b/splunk_add_on_ucc_framework/main.py
@@ -18,6 +18,7 @@
 from typing import Optional, Sequence
 
 from splunk_add_on_ucc_framework import generate
+from splunk_add_on_ucc_framework.commands import init
 
 
 # This is a necessary change to have, so we don't release a breaking change.
@@ -52,9 +53,10 @@ def _parse_known_args(self, arg_strings, *args, **kwargs):
 
 def main(argv: Optional[Sequence[str]] = None):
     argv = argv if argv is not None else sys.argv[1:]
-    parser = DefaultSubcommandArgumentParser(description="Build the add-on")
+    parser = DefaultSubcommandArgumentParser()
     parser.set_default_subparser("build")
-    subparsers = parser.add_subparsers(dest="command")
+    subparsers = parser.add_subparsers(dest="command", description="Build an add-on")
+
     build_parser = subparsers.add_parser("build")
     build_parser.add_argument(
         "--source",
@@ -83,6 +85,34 @@ def main(argv: Optional[Sequence[str]] = None):
         help="Python binary name to use to install requirements",
         default="python3",
     )
+
+    init_parser = subparsers.add_parser("init", description="Bootstrap an add-on")
+    init_parser.add_argument(
+        "--addon-name",
+        type=str,
+        help="Add-on name",
+        required=True,
+    )
+    init_parser.add_argument(
+        "--addon-display-name",
+        type=str,
+        help="Add-on display name",
+        required=True,
+    )
+    init_parser.add_argument(
+        "--addon-input-name",
+        type=str,
+        help="Add-on input name",
+        required=True,
+    )
+    init_parser.add_argument(
+        "--addon-version",
+        type=str,
+        help="Add-on version",
+        default="0.0.1",
+    )
+    init_parser.add_argument("--overwrite", action="store_true", default=False)
+
     args = parser.parse_args(argv)
     if args.command == "build":
         generate(
@@ -91,6 +121,14 @@ def main(argv: Optional[Sequence[str]] = None):
             ta_version=args.ta_version,
             python_binary_name=args.python_binary_name,
         )
+    if args.command == "init":
+        init.init(
+            addon_name=args.addon_name,
+            addon_display_name=args.addon_display_name,
+            addon_input_name=args.addon_input_name,
+            addon_version=args.addon_version,
+            overwrite=args.overwrite,
+        )
 
 
 if __name__ == "__main__":


Test patch:
diff --git a/tests/smoke/test_ucc_generate.py b/tests/smoke/test_ucc_build.py
similarity index 100%
rename from tests/smoke/test_ucc_generate.py
rename to tests/smoke/test_ucc_build.py
diff --git a/tests/smoke/test_ucc_init.py b/tests/smoke/test_ucc_init.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/README.md b/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/README.md
new file mode 100644
index 000000000..40172e8df
--- /dev/null
+++ b/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/README.md
@@ -0,0 +1,1 @@
+# demo_addon_for_splunk
diff --git a/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/globalConfig.json b/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/globalConfig.json
new file mode 100644
index 000000000..1451a60fa
--- /dev/null
+++ b/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/globalConfig.json
@@ -0,0 +1,206 @@
+{
+    "pages": {
+        "configuration": {
+            "tabs": [
+                {
+                    "name": "account",
+                    "table": {
+                        "actions": [
+                            "edit",
+                            "delete",
+                            "clone"
+                        ],
+                        "header": [
+                            {
+                                "label": "Name",
+                                "field": "name"
+                            }
+                        ]
+                    },
+                    "entity": [
+                        {
+                            "type": "text",
+                            "label": "Name",
+                            "validators": [
+                                {
+                                    "type": "regex",
+                                    "errorMsg": "Input Name must begin with a letter and consist exclusively of alphanumeric characters and underscores.",
+                                    "pattern": "^[a-zA-Z]\\w*$"
+                                },
+                                {
+                                    "type": "string",
+                                    "errorMsg": "Length of input name should be between 1 and 100",
+                                    "minLength": 1,
+                                    "maxLength": 100
+                                }
+                            ],
+                            "field": "name",
+                            "help": "A unique name for the data input.",
+                            "required": true
+                        },
+                        {
+                            "type": "text",
+                            "label": "API key",
+                            "field": "api_key",
+                            "help": "API key",
+                            "required": true,
+                            "encrypted": true
+                        }
+                    ],
+                    "title": "Accounts"
+                },
+                {
+                    "name": "logging",
+                    "entity": [
+                        {
+                            "type": "singleSelect",
+                            "label": "Log level",
+                            "options": {
+                                "disableSearch": true,
+                                "autoCompleteFields": [
+                                    {
+                                        "value": "DEBUG",
+                                        "label": "DEBUG"
+                                    },
+                                    {
+                                        "value": "INFO",
+                                        "label": "INFO"
+                                    },
+                                    {
+                                        "value": "WARN",
+                                        "label": "WARN"
+                                    },
+                                    {
+                                        "value": "ERROR",
+                                        "label": "ERROR"
+                                    },
+                                    {
+                                        "value": "CRITICAL",
+                                        "label": "CRITICAL"
+                                    }
+                                ]
+                            },
+                            "defaultValue": "INFO",
+                            "field": "loglevel"
+                        }
+                    ],
+                    "title": "Logging"
+                }
+            ],
+            "title": "Configuration",
+            "description": "Set up your add-on"
+        },
+        "inputs": {
+            "services": [
+                {
+                    "name": "demo_input",
+                    "entity": [
+                        {
+                            "type": "text",
+                            "label": "Name",
+                            "validators": [
+                                {
+                                    "type": "regex",
+                                    "errorMsg": "Input Name must begin with a letter and consist exclusively of alphanumeric characters and underscores.",
+                                    "pattern": "^[a-zA-Z]\\w*$"
+                                },
+                                {
+                                    "type": "string",
+                                    "errorMsg": "Length of input name should be between 1 and 100",
+                                    "minLength": 1,
+                                    "maxLength": 100
+                                }
+                            ],
+                            "field": "name",
+                            "help": "A unique name for the data input.",
+                            "required": true
+                        },
+                        {
+                            "type": "text",
+                            "label": "Interval",
+                            "validators": [
+                                {
+                                    "type": "regex",
+                                    "errorMsg": "Interval must be an integer.",
+                                    "pattern": "^\\-[1-9]\\d*$|^\\d*$"
+                                }
+                            ],
+                            "defaultValue": "300",
+                            "field": "interval",
+                            "help": "Time interval of the data input, in seconds.",
+                            "required": true
+                        },
+                        {
+                            "type": "singleSelect",
+                            "label": "Account to use",
+                            "options": {
+                                "referenceName": "account"
+                            },
+                            "help": "Account to use for this input.",
+                            "field": "account",
+                            "required": true
+                        }
+                    ],
+                    "title": "demo_input"
+                }
+            ],
+            "title": "Inputs",
+            "description": "Manage your data inputs",
+            "table": {
+                "actions": [
+                    "edit",
+                    "enable",
+                    "delete",
+                    "clone"
+                ],
+                "header": [
+                    {
+                        "label": "Name",
+                        "field": "name"
+                    },
+                    {
+                        "label": "Interval",
+                        "field": "interval"
+                    },
+                    {
+                        "label": "Index",
+                        "field": "index"
+                    },
+                    {
+                        "label": "Status",
+                        "field": "disabled"
+                    }
+                ],
+                "moreInfo": [
+                    {
+                        "label": "Name",
+                        "field": "name"
+                    },
+                    {
+                        "label": "Interval",
+                        "field": "interval"
+                    },
+                    {
+                        "label": "Index",
+                        "field": "index"
+                    },
+                    {
+                        "label": "Status",
+                        "field": "disabled",
+                        "mapping": {
+                            "true": "Disabled",
+                            "false": "Enabled"
+                        }
+                    }
+                ]
+            }
+        }
+    },
+    "meta": {
+        "name": "demo_addon_for_splunk",
+        "restRoot": "demo_addon_for_splunk",
+        "version": "5.18.0R2b855ba9",
+        "displayName": "Demo Add-on for Splunk",
+        "schemaVersion": "0.0.3"
+    }
+}
\ No newline at end of file
diff --git a/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/LICENSE.txt b/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/LICENSE.txt
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/README.txt b/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/README.txt
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/app.manifest b/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/app.manifest
new file mode 100644
index 000000000..2906523f5
--- /dev/null
+++ b/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/app.manifest
@@ -0,0 +1,57 @@
+{
+    "schemaVersion": "2.0.0",
+    "info": {
+        "title": "Demo Add-on for Splunk",
+        "id": {
+            "group": null,
+            "name": "demo_addon_for_splunk",
+            "version": "0.0.1"
+        },
+        "author": [
+            {
+                "name": "",
+                "email": null,
+                "company": null
+            }
+        ],
+        "releaseDate": null,
+        "description": "Demo Add-on for Splunk",
+        "classification": {
+            "intendedAudience": "IT Professionals",
+            "categories": [
+                "Security, Fraud & Compliance"
+            ],
+            "developmentStatus": "Production/Stable"
+        },
+        "commonInformationModels": null,
+        "license": {
+            "name": null,
+            "text": "LICENSE.txt",
+            "uri": null
+        },
+        "privacyPolicy": {
+            "name": null,
+            "text": null,
+            "uri": null
+        },
+        "releaseNotes": {
+            "name": "README",
+            "text": "README.txt",
+            "uri": ""
+        }
+    },
+    "dependencies": null,
+    "tasks": null,
+    "inputGroups": null,
+    "incompatibleApps": null,
+    "platformRequirements": null,
+    "supportedDeployments": [
+        "_standalone",
+        "_distributed",
+        "_search_head_clustering"
+    ],
+    "targetWorkloads": [
+        "_search_heads",
+        "_indexers"
+    ]
+}
\ No newline at end of file
diff --git a/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/bin/demo_input.py b/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/bin/demo_input.py
new file mode 100644
index 000000000..a228b40ff
--- /dev/null
+++ b/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/bin/demo_input.py
@@ -0,0 +1,108 @@
+import json
+import logging
+import sys
+import traceback
+
+import import_declare_test
+from solnlib import conf_manager, log
+from splunklib import modularinput as smi
+
+ADDON_NAME = "demo_addon_for_splunk"
+
+
+def logger_for_input(input_name: str) -> logging.Logger:
+    normalized_input = input_name.split("/")[-1]
+    return log.Logs().get_logger(
+        f"demo_addon_for_splunk_{normalized_input}"
+    )
+
+
+def get_account_api_key(session_key: str, account_name: str):
+    cfm = conf_manager.ConfManager(
+        session_key,
+        ADDON_NAME,
+        realm=f"__REST_CREDENTIAL__#{ADDON_NAME}#configs/conf-demo_addon_for_splunk_account",
+    )
+    account_conf_file = cfm.get_conf("demo_addon_for_splunk_account")
+    return account_conf_file.get(account_name).get("username")
+
+
+def get_data_from_api(logger: logging.Logger, api_key: str):
+    logger.info("Getting data from an external API")
+    dummy_data = [
+        {
+            "line1": "hello",
+        },
+        {
+            "line2": "world",
+        }
+    ]
+    return dummy_data
+
+
+class Input(smi.Script):
+    def __init__(self):
+        super().__init__()
+
+    def get_scheme(self):
+        scheme = smi.Scheme("demo_input")
+        scheme.description = "demo_input input"
+        scheme.use_external_validation = True
+        scheme.streaming_mode_xml = True
+        scheme.use_single_instance = False
+        scheme.add_argument(
+            smi.Argument(
+                "name", title="Name", description="Name", required_on_create=True
+            )
+        )
+        return scheme
+
+    def validate_input(self, definition: smi.ValidationDefinition):
+        return
+
+    def stream_events(self, inputs: smi.InputDefinition, event_writer: smi.EventWriter):
+        # inputs.inputs is a Python dictionary object like:
+        # {
+        #   "demo_input://<input_name>": {
+        #     "account": "<account_name>",
+        #     "disabled": "0",
+        #     "host": "$decideOnStartup",
+        #     "index": "<index_name>",
+        #     "interval": "<interval_value>",
+        #     "python.version": "python3",
+        #   },
+        # }
+        for input_name, input_item in inputs.inputs.items():
+            logger = logger_for_input(input_name)
+            try:
+                session_key = self._input_definition.metadata["session_key"]
+                log_level = conf_manager.get_log_level(
+                    logger=logger,
+                    session_key=session_key,
+                    app_name=ADDON_NAME,
+                    conf_name=f"{ADDON_NAME}_settings",
+                )
+                logger.setLevel(log_level)
+                logger.info("Start of the modular input")
+                api_key = get_account_api_key(session_key, input_item.get("account"))
+                data = get_data_from_api(logger, api_key)
+                for line in data:
+                    event_writer.write_event(
+                        smi.Event(
+                            data=json.dumps(line, ensure_ascii=False, default=str),
+                            index=input_item.get("index"),
+                            sourcetype="dummy-data",
+                        )
+                    )
+                logger.info("End of the modular input")
+            except Exception as e:
+                logger.error(
+                    f"Exception raised while ingesting data for "
+                    f"demo_input: {e}. Traceback: "
+                    f"{traceback.format_exc()}"
+                )
+
+
+if __name__ == "__main__":
+    exit_code = Input().run(sys.argv)
+    sys.exit(exit_code)
diff --git a/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/default/app.conf b/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/default/app.conf
new file mode 100644
index 000000000..fa82d7baf
--- /dev/null
+++ b/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/default/app.conf
@@ -0,0 +1,23 @@
+[install]
+is_configured = false
+state = enabled
+build = 1
+
+[launcher]
+description = Demo Add-on for Splunk
+version = 0.0.1
+
+[ui]
+is_visible = true
+label = Demo Add-on for Splunk
+
+[package]
+id = demo_addon_for_splunk
+
+[id]
+name = demo_addon_for_splunk
+version = 0.0.1
+
+[triggers]
+reload.demo_addon_for_splunk_settings = simple
+reload.demo_addon_for_splunk_account = simple
diff --git a/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/default/demo_addon_for_splunk_settings.conf b/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/default/demo_addon_for_splunk_settings.conf
new file mode 100644
index 000000000..f2566d453
--- /dev/null
+++ b/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/default/demo_addon_for_splunk_settings.conf
@@ -0,0 +1,2 @@
+[logging]
+loglevel = INFO
diff --git a/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/default/server.conf b/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/default/server.conf
new file mode 100644
index 000000000..c4ee0787a
--- /dev/null
+++ b/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/default/server.conf
@@ -0,0 +1,3 @@
+[shclustering]
+conf_replication_include.demo_addon_for_splunk_settings = true
+conf_replication_include.demo_addon_for_splunk_account = true
diff --git a/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/lib/requirements.txt b/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/lib/requirements.txt
new file mode 100644
index 000000000..99f4b184d
--- /dev/null
+++ b/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/lib/requirements.txt
@@ -0,0 +1,3 @@
+splunktaucclib
+splunk-sdk
+solnlib
diff --git a/tests/testdata/test_addons/package_global_config_configuration/globalConfig.json b/tests/testdata/test_addons/package_global_config_configuration/globalConfig.json
index 357d8a6fe..89575b822 100644
--- a/tests/testdata/test_addons/package_global_config_configuration/globalConfig.json
+++ b/tests/testdata/test_addons/package_global_config_configuration/globalConfig.json
@@ -421,8 +421,8 @@
     "meta": {
         "name": "Splunk_TA_UCCExample",
         "restRoot": "splunk_ta_uccexample",
-        "version": "5.18.0R9222a5a8",
+        "version": "5.18.0R05a4667a",
         "displayName": "Splunk UCC test Add-on",
         "schemaVersion": "0.0.3"
     }
-}
\ No newline at end of file
+}
diff --git a/tests/testdata/test_addons/package_global_config_inputs_configuration_alerts/globalConfig.json b/tests/testdata/test_addons/package_global_config_inputs_configuration_alerts/globalConfig.json
index a9fbc2a6b..26efd81de 100644
--- a/tests/testdata/test_addons/package_global_config_inputs_configuration_alerts/globalConfig.json
+++ b/tests/testdata/test_addons/package_global_config_inputs_configuration_alerts/globalConfig.json
@@ -1108,8 +1108,8 @@
     "meta": {
         "name": "Splunk_TA_UCCExample",
         "restRoot": "splunk_ta_uccexample",
-        "version": "5.18.0R9222a5a8",
+        "version": "5.18.0R05a4667a",
         "displayName": "Splunk UCC test Add-on",
         "schemaVersion": "0.0.3"
     }
-}
\ No newline at end of file
+}
diff --git a/tests/testdata/test_addons/package_global_config_inputs_configuration_alerts/package/metadata/default.meta b/tests/testdata/test_addons/package_global_config_inputs_configuration_alerts/package/metadata/default.meta
deleted file mode 100644
index 1231cfad5..000000000
--- a/tests/testdata/test_addons/package_global_config_inputs_configuration_alerts/package/metadata/default.meta
+++ /dev/null
@@ -1,7 +0,0 @@
-
-# Application-level permissions
-
-[]
-owner = admin
-access = read : [ * ], write : [ admin, sc_admin ]
-export = system
diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py
index 666b92c65..20c2132d7 100644
--- a/tests/unit/test_main.py
+++ b/tests/unit/test_main.py
@@ -21,7 +21,7 @@
 
 
 @pytest.mark.parametrize(
-    "args,expected_args_to_generate",
+    "args,expected_parameters",
     [
         (
             [],
@@ -142,7 +142,37 @@
     ],
 )
 @mock.patch("splunk_add_on_ucc_framework.main.generate")
-def test_main_with_parameters(ucc_gen_generate, args, expected_args_to_generate):
+def test_build_command(mock_ucc_gen_generate, args, expected_parameters):
     main.main(args)
 
-    ucc_gen_generate.assert_called_with(**expected_args_to_generate)
+    mock_ucc_gen_generate.assert_called_with(**expected_parameters)
+
+
+@pytest.mark.parametrize(
+    "args,expected_parameters",
+    [
+        (
+            [
+                "init",
+                "--addon-name",
+                "splunk_add_on_for_demo",
+                "--addon-display-name",
+                "Splunk Add-on for Demo",
+                "--addon-input-name",
+                "demo_input",
+            ],
+            {
+                "addon_name": "splunk_add_on_for_demo",
+                "addon_display_name": "Splunk Add-on for Demo",
+                "addon_input_name": "demo_input",
+                "addon_version": "0.0.1",
+                "overwrite": False,
+            },
+        ),
+    ],
+)
+@mock.patch("splunk_add_on_ucc_framework.commands.init.init")
+def test_init_command(mock_init_command, args, expected_parameters):
+    main.main(args)
+
+    mock_init_command.assert_called_with(**expected_parameters)