text:
---
layout: post
title: "Trouble Shooting On SpaceWalk OSAD On Ubuntu Clients"
date: 2015-08-12 11:25:34 +0800
comments: true
categories: Virtualization
---
### Problem
On Clients(Ubuntu nodes), you will see lots of the following message in
`/var/log/osad`:

```
$ tail /var/log/osad
2015-08-11 19:31:14 jabber_lib.main: Unable to connect to jabber servers, sleeping 78 seconds
2015-08-11 19:32:32 jabber_lib.main: Unable to connect to jabber servers, sleeping 117 seconds
```
When restart the osda service you will see following error message:

```
# service osad restart
OSAD SpaceWalk Deamon osad                                                     Traceback (most recent call last):
  File "/usr/share/rhn/osad/jabber_lib.py", line 252, in setup_connection
    c = self._get_jabber_client(js)
  File "/usr/share/rhn/osad/jabber_lib.py", line 309, in _get_jabber_client
    c.connect()
  File "/usr/share/rhn/osad/jabber_lib.py", line 583, in connect
    self.disconnect()
  File "/usr/share/rhn/osad/jabber_lib.py", line 653, in disconnect
    jabber.Client.disconnect(self)
  File "/usr/lib/python2.7/dist-packages/jabber/jabber.py", line 432, in disconnect
    xmlstream.Client.disconnect(self)
  File "/usr/lib/python2.7/dist-packages/jabber/xmlstream.py", line 388, in disconnect
    while self.process(): pass
  File "/usr/share/rhn/osad/jabber_lib.py", line 1059, in process
    raise JabberError("Premature EOF")
JabberError: Premature EOF
```
Though seeing this error, your osad will start and running, but with errors.

### Trouble Shooting
Make sure the port 5222 and 5269 of spacewalk server are telnet-able from client machine:

```
adminubuntu@spacewalknode1:~$ telnet spacewalk 5222
Trying <IP_ADDRESS>...
Connected to spacewalk.
Escape character is '^]'.
^]
telnet> quit
Connection closed.
adminubuntu@spacewalknode1:~$ telnet spacewalk 5269
Trying <IP_ADDRESS>...
Connected to spacewalk.
Escape character is '^]'.
^]
telnet>
```

Make sure you register yourself on client using FQDN but not the IP Address:

```
# rhnreg_ks --activationkey=1-precise --serverUrl=http://spacewalk/XMLRPC --force
# service osad restart
```

If by this you won't pass the osad check, you should setup the local dns server, which
enable name resolve of `spacewalk`, you could take following article for reference:
[http://purplepalmdash.github.io/blog/2015/08/05/enable-dhcp-slash-dns-server-for-spacewalker-server/](http://purplepalmdash.github.io/blog/2015/08/05/enable-dhcp-slash-dns-server-for-spacewalker-server/)

For checking the dns resole, run following command on client:

```
adminubuntu@spacewalknode1:~$ dig spacewalk

; <<>> DiG 9.8.1-P1 <<>> spacewalk
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 16045
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 0

;; QUESTION SECTION:
;spacewalk.                     IN      A

;; ANSWER SECTION:
spacewalk.              604800  IN      A       <IP_ADDRESS>

;; AUTHORITY SECTION:
spacewalk.              604800  IN      NS      spacewalk.

;; Query time: 2 msec
;; SERVER: <IP_ADDRESS>#53(<IP_ADDRESS>)
;; WHEN: Wed Aug 12 03:34:18 2015
;; MSG SIZE  rcvd: 57
```

### Result
After resolving this problem, your log should like this:

On Server:

```
$ tail /var/log/message
Aug 12 10:34:50 spacewalk jabberd/c2s[1379]: [9] [::ffff:<IP_ADDRESS>, port=45486] connect
Aug 12 10:34:50 spacewalk named[993]: error (network unreachable) resolving 'ns4.p27.dynect.net/A/IN': 2001:500:94::100#53
Aug 12 10:34:50 spacewalk jabberd/c2s[1379]: [9] created user: user=osad-c942811c1c; realm=
Aug 12 10:34:50 spacewalk jabberd/c2s[1379]: [9] registration succeeded, requesting user creation: jid=osad-c942811c1c@spacewalk
Aug 12 10:34:50 spacewalk jabberd/sm[1371]: created user: jid=osad-c942811c1c@spacewalk
Aug 12 10:34:50 spacewalk jabberd/c2s[1379]: [9] legacy authentication succeeded: host=, username=osad-c942811c1c, resource=osad, TLS negotiated
Aug 12 10:34:50 spacewalk jabberd/c2s[1379]: [9] requesting session: jid=osad-c942811c1c@spacewalk/osad
Aug 12 10:34:50 spacewalk jabberd/sm[1371]: session started: jid=osad-c942811c1c@spacewalk/osad
```
In SpaceWalk backend you will see:

![/images/2015_08_12_11_37_48_538x222.jpg](/images/2015_08_12_11_37_48_538x222.jpg)
Now you could direct `push` your modification to clients in SpaceWalk backend.


text:
# SSRS plugin

[![Build Status][xld-ssrs-plugin-travis-image] ][xld-ssrs-plugin-travis-url]
[![License: MIT][xld-ssrs-plugin-license-image]][xld-ssrs-plugin-license-url]
![Github All Releases][xld-ssrs-plugin-downloads-image]
[![Codacy Badge][xld-ssrs-plugin-codacy-image] ][xld-ssrs-plugin-codacy-url]
[![Code Climate][xld-ssrs-plugin-code-climate-image] ][xld-ssrs-plugin-code-climate-url]

[xld-ssrs-plugin-travis-image]: https://travis-ci.org/xebialabs-community/xld-ssrs-plugin.svg?branch=master
[xld-ssrs-plugin-travis-url]: https://travis-ci.org/xebialabs-community/xld-ssrs-plugin
[xld-ssrs-plugin-license-image]: https://img.shields.io/badge/License-MIT-yellow.svg
[xld-ssrs-plugin-license-url]: https://opensource.org/licenses/MIT
[xld-ssrs-plugin-downloads-image]: https://img.shields.io/github/downloads/xebialabs-community/xld-ssrs-plugin/total.svg
[xld-ssrs-plugin-codacy-image]: https://api.codacy.com/project/badge/Grade/c22530fe75554e8283856e4a5eeed0c5
[xld-ssrs-plugin-codacy-url]: https://www.codacy.com/app/joris-dewinne/xld-ssrs-plugin
[xld-ssrs-plugin-code-climate-image]: https://codeclimate.com/github/xebialabs-community/xld-ssrs-plugin/badges/gpa.svg
[xld-ssrs-plugin-code-climate-url]: https://codeclimate.com/github/xebialabs-community/xld-ssrs-plugin

# Overview

This document describes the functionality provided by the SSRS plugin.

See the **XL Deploy Reference Manual** for background information on XL Deploy and deployment concepts.

## Features

* Deploys SSRS Reports to an MSSQLClient container
* Creates a DataSource and applies that DataSource to all reports in specified path
* Compatible with SQL Server 2005 and 2008

# Requirements

* **XL Deploy requirements**
	* **XL Deploy**: version 5.x+
	* requires the database plugin (see `XL_DEPLOY_SERVER_HOME/available-plugins`)
	* **WinRM Native** *Overthere* connection

# Installation

Place the plugin xldp file into your `XL_DEPLOY_SERVER_HOME/plugins` directory.

# Usage

The `mssql.RSReports` deployable consists of a folder artifact containing all reports. Only files with the default reports extension (*.rdl) are processed.
The plugin creates a datasource based on the properties entered. It then reads the *.rdl files from the provided folder artifact and upload them to the server. After the reports have been uploaded they will be linked to the created dataSource.
The `mssql.RSReports` deployable needs to deploy to a `sql.msSqlClient` container due to the dependency on MS SQL Server.

# Known limitations

* Uses the 2005 (/ReportServer/ReportService2005.asmx) endpoint. This should work with SQL Server 2005 and 2008.
* When undeploying all reports from the serverPath will be removed. Including any reports that may have been there prior to the XL Deploy dep

license: MIT

repository: xebialabs-community/xld-ssrs-plugin

branch_name: refs/heads/master

extension: md

license_type: permissive

repo_name: xebialabs-community/xld-ssrs-plugin


text:
Slack Plugin
============

Send alerts to [Slack](https://slack.com/).

![Slack Message](./images/alerta-slack-plugin.png)

Installation
------------

Run the following command on the Alerta server to install and register the new plugin:

    $ python setup.py install

Configuration
-------------

To configure the Slack plugin start by setting up an
[incoming webhook integration](https://my.slack.com/services/new/incoming-webhook/)
for your Slack channel and adding the following configuration settings to `alertad.conf`:

```
SLACK_WEBHOOK_URL = 'https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX'
SLACK_ATTACHMENTS = True  # default=False
SLACK_CHANNEL = '' # if empty then uses channel from incoming webhook configuration
ALERTA_UI_URL = 'http://your.alerta.ui.url'
ICON_EMOJI = '' # default :rocket:
ALERTA_USERNAME = '' # default alerta

```

Lastly, add `slack` to the list of enabled plugins:

```
PLUGINS = ['reject', 'slack']
```

References
----------

Slack Incoming Webhooks: https://api.slack.com/incoming-webhooks

repository: Atoms/alerta-contrib

repo_name: Atoms/alerta-contrib


text:
spamassassin
============

This plugin implements the spamd protocol and will send messages to
spamd for scoring.

Configuration
-------------

spamassassin.ini

- spamd_socket = \[host:port | /path/to/socket\]  *optional*

    Default: localhost:783

    Host or path to socket where spamd is running.

- spamd_user = \[user\]   *optional*

    Default: default

    Username to pass to spamd.  This is useful when you are running
    spamd with virtual users.

    You can also pass this value in dynamically by setting:

    `connection.transaction.notes.spamd_user`

- max_size = N  *optional*

    Default: 500000

    Maximum size of messages (in bytes) to send to spamd.
    Messages over this size will be skipped.

- reject_thresold = N   *optional*

    Default: none (do not reject any mail)

    SpamAssassin score at which the mail should be rejected.

- relay_reject_threshold = N  *optional*

    Default: none

    As above, except this threshold only applies to connections
    that are relays (e.g. AUTH) where connection.relaying = true.
    This is used to set a *lower* thresold at which to reject mail
    from these hosts to prevent sending outbound spam.

    If this is not set, then the `reject_thresold` value is used.

- munge_subject_threshold = N  *optional*

    Default: none (do not munge the subject)

    Score at which the subject should be munged (prefixed).

- subject_prefix = \[prefix\]   *optional*

    Default: *** SPAM ***

    Prefix to use when munging the subject.

- old_headers_action = \[rename | drop | keep\]   *optional*

    Default: rename

    If old X-Spam-\* headers are in the email, what do we do with them?

    `rename` them to X-Old-Spam-\*.

    `drop` will delete them.

    `keep` will keep them (new X-Spam-\* headers appear lower down in
    the headers then).

Extras
======

A SpamAssassin plugin can be found in the `contrib` directory.
The `Haraka.\[pm|cf\]` files should be placed in the SpamAssassin local
site rules directory (/etc/mail/spamassassin on Linux), spamd should be
restarted and the plugin will make spamd output the Haraka UUID as part
of it's log output to aid debugging when searching the mail logs.

repository: revisionfour/Haraka

repo_name: revisionfour/Haraka


text:
# styletransfer
### No machine learning - the best way
#### Dorian Chan & Michelle Hwang

For transferring styles from a style image to a content image. Based on the
paper [Style-Transfer via
Texture-Synthesis](https://arxiv.org/pdf/1609.03057.pdf) by Michael Elad and
Peyman Milanfar.

Report is [here](paper.pdf).

## Usage

Install dependencies with `pip install -r requirements.txt`.

Import the style_transfer module and use the `style_transfer` function as
documented, or run within the `style_transfer.py` file. See `style_transfer.py`
for example usage.

## Other Files

* `images`: folder containing the content (and mask) and style images we used.
* `outputs`: folder containing some images resulting as outputs of `style_transfer`.
* `patch_matching.py`: For matching patches between images based on their L2 distance.
* `patch_matching_pca.py`: For matching patches between images based on the L2
  distances between their PCA decompositions.
* `robust.py`: For aggregating patches.
* `fusion.py`: For fusing images together.
* `color_transfer.py`: For performing color transfers from a source to target
  image based on histogram matching.
* `denoise.py`: For denoising images.

repository: antiamoeba/styletransfer

repo_name: antiamoeba/styletransfer


text:
OWL2VOWL converter [![Build Status](https://travis-ci.org/VisualDataWeb/OWL2VOWL.svg)](https://travis-ci.org/VisualDataWeb/OWL2VOWL)
==================

Requirements
------------
*   Java 6 (or higher)
*   Maven

Instructions for using OWL2VOWL
------------

The converted ontology will be written in a .json file in the working directory by default.

### Convert an ontology by its IRI
Run owl2vowl.jar with the `-iri` parameter: `java -jar owl2vowl.jar -iri "[Ontology IRI]"`

**Example**: `java -jar owl2vowl.jar -iri "http://ontovibe.visualdataweb.org"`

### Convert an ontology from a local file
Run owl2vowl.jar with the `-file` parameter: `java -jar owl2vowl.jar -file path/to/local/ontology`

**Example**: `java -jar owl2vowl.jar -file ontologies/foaf.rdf`

### Convert an ontology with dependencies from a local file
Run owl2vowl with the additional `-dependencies` parameter: `java -jar owl2vowl.jar -file path/to/local/ontology -dependencies path/to/dependency1 path/to/dependency2 ...`

**Example**: `java -jar owl2vowl.jar -file ontovibe.ttl -dependencies ontovibe_imported.ttl`

### Echo the converted ontology on the console
Use any of the input parameters together with the `-echo` flag: `java -jar owl2vowl.jar -echo -iri "[Ontology IRI]"`

**Example**: `java -jar owl2vowl.jar -iri "http://ontovibe.visualdataweb.org" -echo`

Instructions for developing with OWL2VOWL
-----------

### First steps
`Maven` is required to develop with OWL2VOWL and compile the code. It will load all dependencies automatically. If there occur any problems, refreshing the dependencies might solve them.
OWL2VOWL has been developed to work with Java 6, so `JDK 6` has to be used for compiling.

### Build the jar
To build the jar file, simply execute `mvn package`. The built `jar` only contains the compiled source code.
To build a jar file that can be executed standalone, you have to use the package option with parameter: `mvn package -Denv=standalone`. This will build the file `owl2vowl.jar` containing all dependencies needed to be executable. There will also be a `zip` file created containing the `owl2vowl.jar`, README and the license files.

### Running in IDE
It would be a pain always building the jar only to test some new implemented stuff. We included a possiblity to run the conversion directly with the IDE. For this you need to change the `Main.java` class.

Change the field `CONVERT_ONE = false` to `CONVERT_ONE = true`. Now you can work with the `convertOneOntology`-method to convert those ontologies you would like to test with. Some ontologies for testing can also be found in the `Ontology_Path.java`.

BUT if you want to create the `jar`, you have to change the field back to `CONVERT_ONE = false` - otherwise it will still only execute the `convertOneOntology`-method.


text:
---
title: "Experimental Project"
type: activities
cover: "https://source.unsplash.com/Z7n-qSootxg"
---
import Subpage from "../../components/subpage.js"

<Subpage slug="overall">

### Overall

This third project is an elective project that you allows you to select an advanced topic that interests you for either growing into a new area, or beginning to experiment with technologies or methods that could serve in your capstone project or career. Consider from among the following current trends or topics, in addition to proposing your own:

* CSS transitions and animations.
* Atomic Web Design, Object-oriented CSS, or BEM.
* Interactive design frameworks such as Material Design.
* SVG, responsive SVG, animated SVG.
* JavaScript programming with technologies such as NodeJS, Angular, React, and others.
* Server-side development using technologies such as Ruby on Rails, PHP and MySQL, or Python and PostgreSQL.

What you do in this project, however, will be a little more involved than in the first two. In this one you will complete an actual project as you did in the others---which we'll call the "Experimental Portion" of this project.

But this time we'll add to this that you must also prepare an instructional guide on the topic for your peers to complete later in the course. Your instructional guide should point to resources, include some steps for learning, provide some starting base for a small challenge project, and challenge participants to actively learn some of what you've learned by completing your guide and finishing the challenge.

You will therefore have two Github repos for this project:

- *Your experimental portion* will be in one repo. In the end this will contain your finished, portfolio-ready project showing what you learned for this project. Work on a `develop` branch and make PRs into `master`. Be sure to invite your professor to this repo and add them as a Reviewer to provide input before any merges.
- *Your instructional portion* will be in a second repo. In the end this will contain at the least the instructions for your peers to follow, along with other resource files as needed, such as sample assets or starter files. Place your instructions in a `README.md` file (which you can edit in any code editor: see [GitHub's guide to Markdown](https://guides.github.com/features/mastering-markdown/) as needed). This must be a public repo so that you can send your peers to it in order to complete your instructional lesson.

Here is the rough schedule:

* Week 1: Propose a topic to your professor through Slack along with samples of sources you will use as a base for your own learning.
* Week 2: Complete substantive project on your own experimental portion of the project.
* Week 3: Finish your experimental portion and submit it through a pull request on Github.
* Week 4: Finish your instructional portion and post it on Github. Share the repo URL for your peers on Slack.
* Weeks 5--6: Choose at least two of your peers' lessons to complete, at least one per week. See the course schedule for exact timing in the semester this course is offered.

Because you will be building an actual instructional portion you do not need to submit resources for this project as you did for the other two.

</Subpage>

gha_language: JavaScript

gha_license_id: MIT


text:
# `MsgUnbanUser`
This message allows you to unban a user in a subspace.

## Structure
```json
{
  "@type": "/desmos.subspaces.v1beta1.MsgUnregisterUser",
  "subspace_id": "<ID of the subspace from where the user will be unbanned>",
  "user": "<Desmos address of the user that will be unbanned>",
  "admin": "<Desmos address of the admin that will unban the user>"
}
```

### Attributes
| Attribute | Type | Description |
| :-------: | :---: | :--------- |
| `subspace_id` | String | ID of the subspace from where the user will be unbanned |
| `user` | String | Desmos address of the user that will be unbanned |
| `admin` | String | Desmos address of the admin that will unban the user |

## Example
```json
{
  "@type": "/desmos.subspaces.v1beta1.MsgBanUser",
  "subspace_id": "19de02e105c68a60e45c289bff19fde745bca9c63c38f2095b59e8e8090ae1af",
  "user": "desmos1tqzrfy9ujrk883e2wezsumyvq64gcm65vhdyr7",
  "admin": "desmos14dz9drkw0dyagnht5fnj6s63cwpxxkw8zsx7x9"
}
```

## Message action
The action associated to this message is the following:

````
unban_user
````


text:
# lavarel-passport-purge-command
This command purges expired access/refresh tokens for Laravel Passport to not overload your database

## Installation
* Install the command with composer :
```
composer require "pierrocknroll/lavarel-passport-purge-command @dev"
```

* For Laravel <= 5.4, add the command to your /config/app.php in the providers array :
```
\Pierrocknroll\LavarelPassportPurgeCommand\ServiceProvider::class
```
For Laravel >= 5.5, the package is auto discovered.

## Usage
```
php artisan passport:purge
```

### Options
```
--days : Number of days the tokens must be expired, default 30.
```

gha_language: PHP


text:
# Initializing a module

You'll need to initialize a module with eggs before publishing it. To do this, you need to type the following command in the root directory of your project:
```shell script
eggs init
```
By doing this, you'll be prompted with a setup screen, where you will be asked to enter:
#### module name
The name of your new module
#### module description
The description of your new module
#### Is this a stable version?
Whether the current version of your module is stable or not. You will be able to update this with each new publish.
#### Enter the files and relative directories that nest.land will publish separated by a comma.
All the files you want to publish with your module. You should include your `README.md` file here next to all the imported / required files of your module.
#### Config format
Choose between your preferred config format: yaml or json.

repository: michael-spengler/docs

repo_name: michael-spengler/docs


text:
---
id: 64f0e2d5-2c83-43df-9144-40f2c68935aa
title: Setup Dendron Development Environment
desc: ''
updated:<PHONE_NUMBER>850
created:<PHONE_NUMBER>825
---
## Build

### Prerequisites

Before you begin, you need to make sure to have the following SDKs and tools:

- [Node.js >= 12.0.0](https://nodejs.org/download/release/latest-v10.x/)
  - We recommend using a version in [Active LTS](https://nodejs.org/en/about/releases/)
- yarn
  - `npm install -g yarn`
- lerna
  - `npm install -g lerna`
- a different instance of VSCode
  - because debugging VSCode by default loads up all extensions that are installed, you'll have issues trying to develop Dendron if you also have VSCode installed . the workaround is to install an alternative flavor of vscode (eg. [insider edition](https://code.visualstudio.com/insiders/)) to use for development

### Steps

1. Clone clone
   ```bash
   git clone https://github.com/dendronhq/dendron.git
   cd dendron
   ```

2. Install dependencies

   ```bash
   # this should install all dependencies
   yarn setup

   # if the above script errors out, you can diagnose the issue and run the following scripts sequentially dependeing on where the error occured
   yarn # install package root dependencies
   yarn bootstrap:bootstrap # install package dependencise
   yarn bootstrap:build  # build package dependencies
   ```

## Run

1. Open the workspace.  At the root of the monorepo, open `dendron-main.code-workspace`. Open this with VSCode to start editing. While its not required to use VSCode, most of the helper scripts in this repository are created with VSCode in mind so using it will make development significantly easier.
2. Debug

- NOTE: you don't need to do this if you are not directly working on the extension (eg. you're working on the [[server|pkg.dendron-next-server]])

To start an instance of the Dendron with the Debugger, Run `Extension: Local (plugin-core)` from the debug panel in vscode

![debugger](/assets/images/start_debugger.gif)

_Note: Running via Run -> Start Debugging will not work unless you've previously targeted `Extension: Local (plugin-core)`_

_Note: To have the changes reflected as you edit the code you need to run the `./bootstrap/scripts/watch.sh` and restart the `Extension: Local (plugin-core)`)_

## Remote Development

If you are developing Dendron in a remote environment using VSCode, see additional instructions [[here|dendron.dev.remote]].

## Normal Workflow

1. Checkout a feature branch for your task
2. Work on code
3. Submit a [[pull Request|dendron.dev.pull-request]]

## Logging

Logs are important for debugging and we should strive to have logs that will give enough information that we can debug any errors from the logs alone. Because we're dealing with users personal notes, we also need to ensure not to log any sensitive information.

If you need to log data, use one of the following loggers depending on what package you are in:

- plugin-core: use [Logger](https://github.com/dendronhq/dendron/blob/master/packages/plugin-core/src/logger.ts#L22:L22)
- react component: use [createLogger](https://github.com/dendronhq/dendron/blob/master/packages/common-frontend/src/utils/logger.ts#L3:L3) in common-frontend
- everything else: use [createLogger](https://github.com/dendronhq/dendron/blob/master/packages/common-server/src/logger.ts#L36:L36) in common-server

Also note that many objects (eg. commands, pods, etc), have an instance of the logger attached to them. You should use them whenever available. They are usually attached as a `.L` or `.logger` property.

- pods have loggers attached in `this.L`

Some things to keep in mind:

- Do not use `console.log` to log data
- Raw note objects contain note content which we never want to log. Instead, use [`NoteUtils.toLogObj`](https://github.com/dendronhq/dendron/blob/master/packages/common-all/src/dnode.ts) which strips out sensitive fields to the logger.

## Exceptions

When handling a Dendron specific error, use the [DendronError](https://github.com/dendronhq/dendron/blob/master/packages/common-all/src/error.ts) class

Some things to keep in mind:

- when returning an error from the server, note that you'll need to convert the error to a plain object using [error2PlainObject](https://github.com/dendronhq/dendron/blob/master/packages/common-all/src/error.ts#L110)
- when displaying an error in a string, you'll need to convert the error using [stringifyError](https://github.com/dendronhq/dendron/blob/master/packages/common-all/src/error.ts#L106:L106)
- whenever possible, attach a [status](https://github.com/dendronhq/dendron/blob/master/packages/common-all/src/constants.ts#L18:L18) to an error
- whenever possible, attach a [severity](https://github.com/dendronhq/dendron/blob/master/packages/common-all/src/constants.ts#L45) to an error

## Project Specific Docs

To see documentation for developing with any specific package, please see `pkg.{name}.dev`.

You can find a list of all packages [[here|dendron://dendron.dendron-site/pkg]].

## Common Operations

- changing or updating a configuration: [[Config|dendron://dendron.dendron-site/pkg.dendron-plugin.dev#config]]

### Working with the API Server

Dendron connects to a local express server which is responsible for indexing your notes. This express server also serves up static files generated by [[Dendron Next Server|pkg.dendron-next-server]].

Dendron compiles the static assets from the next server to the express server during publication so that everything is bundled when published. When you are developing, you can launch the next server independently for faster development. You can follow the instructions [[here|pkg.dendron-next-server.quickstart]] to start the next-server.

To start Dendron with the next server active, you can set the following value in `dendron.yml`

```yml
dev:
  nextServerUrl: "http://localhost:3000"
```

### Working with the CLI

Symlink `dendron`

```sh
cd packages/dendorn-cli
npm link

npm link -g @dendronhq/dendron-cli
```

## Advanced

### Verdaccio

![[dendron.dev.cook.common#verdaccio,1:#*]]

### Linking all Packages

This goes over symlinking all packages locally. This requires that the `meta.json` file be generated. It is created as part of `yarn lerna:bootstrap`.

```sh
node bootstrap/scripts/linkAll.js
```

## Tips

### Watch Monorepo

To continuously compile all dependencies, run the following

```bash
./bootstrap/scripts/watch.sh
```

### Adding dependencies

When you are merging new changes, note that new dependencies and sometimes packages will be installed.

#### New Dependencies

```sh
# install all new dependencies
lerna bootstrap
```

#### New Package in Dendron Mono Repo

Adding new packages is a rarer event but might require a workspace rebuild

```sh
# clean up old files (this might take a few minutes)
./bootstrap/scripts/cleanup.sh

# install all dependencies
lerna bootstrap

# build all dependencies
./bootstrap/scripts/build.sh
```

## Troubleshooting

### Something went wrong during the build

In case something something goes wrong with a build step or you want to save time by not running everything, `init.sh` is just a thin wrapper around the following scripts, each of which can be run individually

- ./bootstrap/scripts/bootstrap.sh: `lerna bootstrap all packages`
- ./bootstrap/scripts/build.sh: `lerna build all packages`

### Changes not showing up in Dendron

1. Are you using the `Run extension` command on the debugger panel to test dendron?

2. If you have one vscode instance which you have dendron installed and are also doing dendron development on, you might get a version conflict. in that case, use `Run extension with plugin disabled` in the debugger panel (or use a different version of vscode to run dendron vs develop)

### Husky Hooks not running

Make sure you run `yarn` at the root of your workspace

## Getting Help

Dendron is actively being developed and it could be quite confusing to start developing for the first time. In this case, we recommend asking for help with whatever blockers you might have with setting up or understanding part of the codebase.

Generally, a member of the Dendron team or the community will chime in for help if you post a specific question in the `#dev` channel in our Discord server.

For more information, check out our handbook entry on `Getting help for development` described [here](https://handbook.dendron.so/notes/bHWjVTtdOCMMRd2_QD0tb.html)

repository: bdefore/dendron-site

repo_name: bdefore/dendron-site


text:
sandboxd
==================

sandboxd uses Linux namespaces to create sandboxes for executing code.
Unlike most sandboxes, it supports all systems calls and spawning new processes - and makes sure that
spawned processes are dead after main finishes (using PID NS).

sandboxd listens on Unix socket:

```
# run these commands as as root
# not needed, but helps preventing collisions
groupadd --gid 999 sandboxd
useradd --uid 999 --gid sandboxd sandboxd
# start server
python sandbox_d.py /path_to_listen_on
```

Make sure to set appropriate unix permissions on socket, so unauthorized users won't be able
to access sandbox.

Protocol
------------------

sandboxd expects that first line in the incoming connection will contain JSON encoded options.
Currently the only supported option is `timeout` - after process runs for `timeout` seconds it
will be killed with SIGKILL.

```
{"timeout": 5.5}
```

After newline character, TAR archive data should follow. This archive will be unpacked to
directory `/home/user` (in chroot, won't affect host filesystem). Then `/home/user/init` will
be run, with stdout and stderr redirected to socket.

Example
---------------------

```
import sandbox_client

box = sandbox_client.Sandbox('/path_to_connect_to')
box.tar.add('example.sh', arcname='init')
box.timeout = 3
box.start()
for line in box.output:
    print '[inferior]', line.rstrip()
```

repository: zielmicha/sandboxd

repo_name: zielmicha/sandboxd


text:
# Lab for Week 1, Session 1 - Setting Up Your Environment

The primary aim of this seminar is to get your environment set up so you can complete the rest of the module.

Once you have your environment set up, you should read some background material explaining [3D Graphics](../graphicsBackground.md), since that will help you get a much better understanding of the core concepts that make [Unity](https://unity3d.com/) work.

## Overview

This session requires you to setup a [GitHub](https://github.com/) account, install [Unity](https://unity3d.com/unity/qa/lts-releases) and then do some [background reading](#3d-graphics).

### GitHub

You should create a student account on [GitHub](https://github.com/) (if you have not done so already), and create a _private_ repository for your P3D work. Finally, you should add the [Module Convenor](https://github.com/glowkeeper/P3D#maintainer) as a collaborator to that repository (GitHub username _glowkeeper_), so they can support you throughout the module.

If you have not used GitHub before, then there is a [Panopto recording](https://sussex.cloud.panopto.eu/Panopto/Pages/Viewer.aspx?id=57307baa-f78e-42a8-8e5c-ac40012ddc4a) of a lecture about GitHub that the  [Module Convenor](https://github.com/glowkeeper/P3D#maintainer) gave earlier this year. You also have access to the [lecture slides](../githubPresentation.pdf).

### Unity

This module builds on some of your previous experience with Unity, further exposing you to an industry-leading platform for creating and operating interactive, 3D content. Therefore, the expectation is that you will have used Unity already, so you probably do not have to do much here. However, you should ensure you have the latest version of [Unity with long term support](https://unity3d.com/unity/qa/lts-releases).

### 3D Graphics

Please read [3D Graphics](../graphicsBackground.md), which introduces some core graphics concepts that will help you throughout the rest of the module.

**The assignment will almost certainly ask you to demonstrate an understanding of some of the material introduced in [3D Graphics](../graphicsBackground.md).**


text:
# clone-deep [![NPM version](https://img.shields.io/npm/v/clone-deep.svg)](https://www.npmjs.com/package/clone-deep) [![Build Status](https://img.shields.io/travis/jonschlinkert/clone-deep.svg)](https://travis-ci.org/jonschlinkert/clone-deep)

> Recursively (deep) clone JavaScript native types, like Object, Array, RegExp, Date as well as primitives.

The `instanceClone` function is invoked to clone objects that are not "plain" objects (as defined by [](#isPlainObject)`isPlainObject`) if it is provided. If `instanceClone` is not specified, it will not attempt to clone non-plain objects, and will copy the object reference.

## Install

Install with [npm](https://www.npmjs.com/)

```sh
$ npm i clone-deep --save
```

## Usage

```js
var cloneDeep = require('clone-deep');

var obj = {a: 'b'};
var arr = [obj];

var copy = cloneDeep(arr);
obj.c = 'd';

console.log(copy);
//=> [{a: 'b'}]

console.log(arr);
//=> [{a: 'b', c: 'd'}]
```

## Other object utils

* [assign-deep](https://www.npmjs.com/package/assign-deep): Deeply assign the enumerable properties and/or es6 Symbol properies of source objects to the target… [more](https://www.npmjs.com/package/assign-deep) | [homepage](https://github.com/jonschlinkert/assign-deep)
* [extend-shallow](https://www.npmjs.com/package/extend-shallow): Extend an object with the properties of additional objects. node.js/javascript util. | [homepage](https://github.com/jonschlinkert/extend-shallow)
* [merge-deep](https://www.npmjs.com/package/merge-deep): Recursively merge values in a javascript object. | [homepage](https://github.com/jonschlinkert/merge-deep)
* [mixin-deep](https://www.npmjs.com/package/mixin-deep): Deeply mix the properties of objects into the first object. Like merge-deep, but doesn't clone. | [homepage](https://github.com/jonschlinkert/mixin-deep)
* [mixin-object](https://www.npmjs.com/package/mixin-object): Mixin the own and inherited properties of other objects onto the first object. Pass an… [more](https://www.npmjs.com/package/mixin-object) | [homepage](https://github.com/jonschlinkert/mixin-object)
* [shallow-clone](https://www.npmjs.com/package/shallow-clone): Make a shallow clone of an object, array or primitive. | [homepage](https://github.com/jonschlinkert/shallow-clone)

## Running tests

Install dev dependencies:

```sh
$ npm i -d && npm test
```

## Contributing

Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/jonschlinkert/clone-deep/issues/new).

## Author

**Jon Schlinkert**

* [github/jonschlinkert](https://github.com/jonschlinkert)
* [twitter/jonschlinkert](http://twitter.com/jonschlinkert)

Based on [mout's](https://github.com/mout/mout) implementation of deepClone.

## License

Copyright © 2014-2015 [Jon Schlinkert](https://github.com/jonschlinkert)
Released under the MIT license.

***

_This file was generated by [verb](https://github.com/verbose/verb) on December 23, 2015._

<!-- deps:helper-lookup-deps -->

repository: GenaAiv/portfolio-website

branch_name: refs/heads/main

repo_name: GenaAiv/portfolio-website


text:
The sideNav component sits on the left side of the screen. It contains by default one link to the homepage.

It can be extended with more items by passing the `<li>` tags as children.

For more info, please refer to the component implementation

Due to the sidebar frame styleguide layout, the sidenav component example will be ommitted.

repository: tpiersall/sugui

repo_name: tpiersall/sugui


text:
# Get Readings from an Airthings Wave and publish to MQTT server

[Airthings](http://airthings.com) makes a BTLE Radon detector called "Wave". This is an executable intended to be called periodically from Cron or some other scheduler to publish readings to an MQTT server.

## Limitations

This application doesn't implement 'find' as provided in the example at https://airthings.com/raspberry-pi/

This is just a python API with a main loop that I happen to use in my own Home-Assistant setup. If you're looking for a plug and play solution, consider [balena-airthingswave](https://github.com/renemarc/balena-airthingswave) - @renemarc has created his appliance package using airthingswave-mqtt.

## API

```Python
class AirthingsWave:
    def __init__(self, config_file):
```

Class instantiation requires a path to a config file in YAML format.

```
mqtt:
  broker: <IP_ADDRESS>
  port: 1883
  username:
  password:

waves:
  - name: "basement-radon"
    addr: 98:07:2d:43:4d:ff
```

Before taking a reading, you should:

```
def ble_connect(self, addr):
```

Then you can:

```
def get_readings(self, p):
def publish_readings(self, name, readings):
```

## Example

From \__main\__.py:
```python
c = sys.argv[1]

atw = airthingswave.AirthingsWave_mqtt(c)

count = len(atw.waves)
if count > 0:
    i = 0
    while i < count:
        handle = atw.ble_connect(atw.waves[i]["addr"])
        r = atw.get_readings(handle)
        atw.ble_disconnect(handle)
        atw.publish_readings(atw.waves[i]["name"], r)
        i = i+1

return True
```

repository: RomanGar/airthingswave-mqtt

repo_name: RomanGar/airthingswave-mqtt


text:
---
layout: page
title: About
permalink: /about/
---

Hello, welcome to my website!

My name is Ian Slay.  I have a keenness to tinker and create.

Currently I am an electronics student at Miami Dade College.  As of June 2020 I received my Electronics Engineering Technology Associate in Science Degree.

I made this website to provide a structured archive to log my projects and ideas.  If you would like to collaborate or have any questions, visit my contact page.

ianslay.com is using:
 [GitHub Pages](https://pages.github.com/) to host.
 [Jekyll](https://jekyllrb.com/) to build.
 The [Hyde](https://github.com/poole/hyde) Jekyll theme.

gha_language: CSS

gha_license_id: NOASSERTION


text:
  Running   |
  Features   |
  Getting Started   |
  Contributing   |
  License

The dev.finances is a financial control application.

In the application you can register and delete transactions and see the balance of incomings and expenses of your account.

## 📈 Running

 - See the project running here.
## ⚛ Features

- **HTML**
- **CSS**
- **Javascript**
## 💻 Getting started

 - Just clone this repo using: `git clone https://github.com/luizDorval/maratona-discover.git`

## 🤝 Contributing

Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**.

1. Fork the Project
2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
3.  Push to the Branch (`git push origin feature/AmazingFeature`)
5. Open a Pull Request

## :memo: License

This project is licensed under the MIT License - see the [LICENSE](https://opensource.org/licenses/MIT) page for details.

repository: luizDorval/dev-finances

repo_name: luizDorval/dev-finances


text:
# NuclearCraft

**Note:The double set of brackets in functions, `([...])` is necessary.**
This is basically because all NuclearCraft CraftTweaker methods require an array of objects, be they ingredients, strings, integers or doubles.

All recipes involve five sets of information - item inputs, fluid inputs, item outputs, fluid outputs and extra info. The
first four are clearly the ingredients and products involved in the recipe, and the extra info contains data such as
processing time and power for machines, the base lifetime, power and heat variable of fusion combos, etc.

All parts of the recipe are simply listed in the method - the internal NuclearCraft code will deal with splitting it up into those
five categories and packaging the information up into a recipe.

## Recipe Additions
Recipe Methods will specify `itemInput` for Item Inputs.

Recipe Methods will specify `itemOutput` for Item Outputs.

Recipe Methods will specify `fluidInput` for Fluid Inputs.

Recipe Methods will specify `fluidOutput` for Fluid Outputs.

Recipe Methods will specify `blockInput` for Block Inputs.

Recipe Methods will specify `blockOutput` for Block Outputs.

**Note: `blockInput` and `blockOutput` must be the `IItemStack`/`IIngredient` versions of blocks**

### Item Inputs
`IItemStack`: `<minecraft:gunpowder>` * 4

`IOreDictEntry`: `<ore:ingotIron>` * 2

`null`: null

### Item Outputs
`IItemStack`: `<minecraft:gunpowder>` * 4

`ChanceItemStack`: `<minecraft:diamond>` * 3, 50, 1

`IOreDictEntry`: `<ore:ingotIron>` * 2

`ChanceIOreDictEntry`: `<ore:gemDiamond>` * 5, 75

`null`: null

### Fluid Inputs
`ILiquidStack`: `<liquid:lava>` * 1500

`null`: null

### Fluid Outputs
`ILiquidStack`: `<liquid:lava>` * 1500

`ChanceLiquidStack` : `<liquid:water>` * 2000, 40, 250, 500

`null`: null

### Block Inputs
`IItemStack`: `<minecraft:dirt>` * 4

`IOreDictEntry`: `<ore:blockIron>` * 2

`null`: null

### Block Outputs
`IItemStack`: `<minecraft:dirt>` * 4

`IOreDictEntry`: `<ore:blockIron>` * 2

`null`: null

## Recipe Removals
When specifying a recipe to remove, all that is required is for either all input or output ingredients to be specified.
Ingredient chance data is not required. Again, the items must come first, followed by the fluids.

You may also want to remove all of a certain type of recipe - to do this, simply use the `removeAllRecipes()` method.

## Chance Ingredients
Item and fluid outputs can have additional info attached to them - specifically, info that can randomise the output stack
size somewhat. This additional info is simply tagged onto the end of the specified ingredient.

The size of the stack produced on each process is randomly assigned from a binomial distribution specified by the chance
info. The percentage and ingredient stack size play the roles of the probability and number of trials, respectively.

For ChanceFluidStacks, a 'stack difference' must also be specified, which determines the difference in size between the
possible stacks (for ChanceItemStacks and ChanceOreStacks, this is effectively 1). For example, a ChanceFluidStack for
an ingredient of size 500, with a stack difference of 150 and minimum stack size of 50, will produce 50, 200, 350 or 500
millibuckets of the fluid.

### ChanceItemStack
The form of a `ChanceItemStack` is `IItemStack output, int percentage, @Optional int minimumStackSize`.

The chance for the `output` to be produced is `percentage`.

If not specified, `minimumStackSize` is 0.

### ChanceOreDictEntry
The form of a `ChanceOreDictEntry` is `IOreDictEntry output, int percentage, @Optional int minimumStackSize`.

The chance for the `output` to be produced is `percentage`.

The minimum stack size for the output is `minimumStackSize`. If not specified, it defaults to 0.

### ChanceLiquidStack
The form of a `ChanceLiquidStack` is `ILiquidStack, int percentage, int stackDifference, @Optional int minimumStackSize`.

The chance for the output to be produced is `percentage`.

The difference between possible stack sizes is `stackDifference`.
The minimum stack size for the output is `minimumStackSize`. If not specified, it defaults to 0.

- Example: ``<liquid:ethanol>` * 500, 100, 150, 50
	- The Machine will produce 50, 200, 350 or 500 millibuckets of ethanol

repository: kindlich/CraftTweaker-Documentation

repo_name: kindlich/CraftTweaker-Documentation


text:
# @harmonize/indent

Harmonize's indent was created to bring peace to the developer community by resolving the "tab vs space" debate for good.

license: ISC

repository: schtauffen/harmonize-indent

repo_name: schtauffen/harmonize-indent


text:
# Programmable Hex Standoff

Project was created in [OpenSCAD](https://github.com/openscad/openscad)

![Default view of hex standoff](images/base.jpg)

You can configure parameters of:
- top pin
- distance between the halves of a top pin
- base
- bottom hole

Just play with variables described on top
of [hex-standoff.scad](files/hex-standoff.scad)
file and get result which you need.

## Defaults samples (in millimeters)

### Base

STL file can be found [here](files/hex-standoff_base.stl).

| Part        | Height | Width |
|-------------|--------|-------|
| Top Pin     | 5      | 3     |
| Distance    | 5      | 0.8   |
| Base        | 24     | 6     |
| Bottom Hole | 4      | 3     |

### Foot cap

STL file can be found [here](files/hex-standoff_foot.stl).

![Default view of hex standoff](images/foot.jpg)

| Part        | Height | Width |
|-------------|--------|-------|
| Top Pin     | 5      | 3     |
| Distance    | 5      | 0.8   |
| Base        | 5      | 6     |
| Bottom Hole | 4      | 3     |

### Head cap

STL file can be found [here](files/hex-standoff_head.stl).

![Default view of hex standoff](images/head.jpg)

| Part        | Height | Width |
|-------------|--------|-------|
| Base        | 5      | 6     |
| Bottom Hole | 5      | 3     |

## Similar projects from ThingVerse

* https://www.thingiverse.com/thing:1528494 (my inspiration)
* https://www.thingiverse.com/thing:2761026
* https://www.thingiverse.com/thing:3251614
* https://www.thingiverse.com/thing:2884417

## Links

* $this project was created as part of [Kubernetes cluster on ARM](https://github.com/EvilFreelancer/kubernetes-cluster-on-arm)
* YouTube [Openscad : Simple Hexagon shape](https://www.youtube.com/watch?v=KAKEk1falNg)

repository: EvilFreelancer/hex-standoff

repo_name: EvilFreelancer/hex-standoff


text:
dream-cheeky-api
================

Nodejs API for controller dream cheeky missile launcher.
This is a reinforcement of [pathikrit's *node-thunder-driver* library](https://github.com/pathikrit/node-thunder-driver), with some unexpected behaviours removed and extra features. Now supports Node v16.

____________________
Prerequisite
==============
For module usb and linux: `sudo apt-get install build-essential libudev-dev`

Installation
==============
1. `npm install dream-cheeky-driver`
2. In your js file, add `var DCDriver = require('dream-cheeky-driver');`

____________________

API
==============

###DCDriver
####DCDriver.DEVICE_CONSTANTS
**Description:**

A JS object containing constants for Dream Cheeky Thunder Launcher.

####DCDriver.turnOnDebugMode()
**Description:**

Turn on debug mode (level 4) of USB.

####DCDriver.turnOffDebugMode()
**Description:**

Turn off USB debug mode (ie. debug level 0).

####DCDriver.moveUp(`durationMS`)
**Description:**

Move up for a period of time.

**Parameters:**

#####`durationMS`
Type: Number

####DCDriver.moveDown(`durationMS`)
**Description:**

Move down for a period of time.

**Parameters:**

#####`durationMS`
Type: Number

####DCDriver.moveLeft(`durationMS`)
**Description:**

Move left for a period of time.

**Parameters:**

#####`durationMS`
Type: Number

####DCDriver.moveRight(`durationMS`)
**Description:**

Move right for a period of time.

**Parameters:**

#####`durationMS`
Type: Number

####DCDriver.stop()

Stop movement immediately.

**Parameters:**

####DCDriver.fire(`numberOfShot`)

Shoot for `numberOfShot` times consecutively.

**Parameters:**

#####`numberOfShot`
Type: Number

Number of shots

####DCDriver.park()

Go back to a fixed default location (leftmost and bottommost position).

**Parameters:**

repository: MagnusNordin/dream-cheeky-api

repo_name: MagnusNordin/dream-cheeky-api


text:
---
title: "Tagging and Auditing"
date: 2019-05-21
author: [ "Richard Cheney" ]
description: "Enable default resource tagging without compromising innovation using the append and audit effects."
weight: 5
series:
 - policy
menu:
  side:
    parent: policy-initiative
---

## Introduction

Tagging is important in a governed environment and is one of the areas that can be crucial in managing your inventory of resources well. It is also very easy to set up a set of tagging policies that deny the creation of resources and quickly become difficult to live with. We will take a different line here with a set of policies that don't inhibit innovation and do some of the heavy lifting for us, but also remind us to remediate any non-compliant resources.

In this lab we will use the **Modify** effect which is the best way to manage tag values. Modify is also useful for configuring resources, such as setting the user assigned managed identity on compute. Note that Modify can only do this where aliases have been configured.

Note that Modify uses managed identity so you wil need to be able to assign RBAC roles

We will use the builtin tag policies where they fit our requirement and group them in a custom policy initiative. (Policy initiatives used to be called policy sets and you will see remnants of that naming convention in the CLI commands and resourceIds.)

We will define the initiative at a management group level so that all subscriptions underneath that management group can assign the initiative. We will also define any custom policies at this level. If you cannot work at that level then just work at subscription level.

## Management Groups

So far we have been creating our custom policies at the subscription scope. In this lab we will move up a level and define our custom policies at the management group level.

This assumes you have the permissions to do so. If any of the following commands fail then you have the [fallback options](#fallback-options)

1. Create a management group

    ```bash
    az account management-group create --name policytest --display-name "Policy Testing"
    ```

1. Move your subscription under the management group

    It is assumed that you are using your own personal subscription for this lab.

    Go back up to the root tenant group and click on the ellipsis (**...**) to the right of your subscription.

    Click on Move and then select the Policy Testing management group from the drop down list.

## Fallback options

If everything worked then skip to the [next section](#requirement). If anything failed then you do not have sufficient access.

Here are your fallback options:

1. To complete the labs as written then ideally you be assigned **Owner** at Tenant Root Group
1. If not, ask the Owner to create the _policytest_ management group, move your subscription to it and assign the following roles

    * **Management Group Contributor**
    * **Resource Policy Contributor**
    * **User Access Administrator**

1. Finally, if none of the above are possible then just work at the subscription scope, assuming you are Owner at that level

## Requirement

As part of our governance, the following tags are required on all resource groups and resources.

* Owner
* Department
* Application
* Environment
* Downtime
* Costcode

Resource groups should ideally have the first three specified. If not then they will be created with an empty value and fail subsequent compliancy audits.

Department must be selected from a list of permitted values, which will be specified at assignment as an array of strings.

Costcode and Environment will be set at the subscription level and inherited if not specified. We will allow these to deviate from the subscription level default values so that specific resource groups can be used for other costcodes.

Downtime will always use a default of "Tuesday, 04:00-04:30" if unspecified.

Resources will always inherit all six tags from the resource group. Resources should never have different tag values to the resource group that they are in.

## Builtin policies

Browse the [policy definitions](https://portal.azure.com/#blade/Microsoft_Azure_Policy/PolicyMenuBlade/Definitions) and filter Category to _Tags_.

Ignore those that mention "Require" or "Append" in the description and instead check out those that have "Add" or "Inherit". They usually make use of the Modify effect.  Review the JSON to understand the logic.

In this lab we will use the following builtin policies:

| **GUID** | **Descriptions** |
|---|---|
| 40df99da-1232-49b1-a39a-6da8d878f469 | Inherit a tag from the subscription if missing |
| 96670d01-0a4d-4649-9c89-2d3abc0a5025 | Require a tag on resource group |
| cd3aa116-8754-49c9-a813-ad46512ece54 | Inherit a tag from the resource group |
| 726aca4c-86e9-4b04-b0c5-073027359532 | Add a tag to a resource group |
| 2a0e14a6-b0a6-4fab-991a-187a4f81c498 | Append a tag with a default value |

We will want some specific audit policies - one to check for empty strings, and another to check that it is a string that is one of an array of permitted values - that do not exist as builtin policies, so we'll add those later.

You can use the CLI to view a definition: `az policy definition show --name <GUID>`

The full resourceId for a builtin policy is `/providers/Microsoft.Authorization/policyDefinitions/<GUID>`.

## Initial policy initiative

We'll now iterate on an initial version of the custom policy initiative, focusing on setting tags at the resource group level. The custom policies and resource group inheritance will be added later.

1. Review the policy initiative structure

    Take a look at the policy initiative structure docs: <https://docs.microsoft.com/azure/governance/policy/concepts/initiative-definition-structure>

    Note that it contains a number of properties:

    * display name
    * description
    * metadata
    * parameters
    * policy definitions

    > We will ignore the policy groups as they are for more formal compliancy levels.

    The metadata will determine where we see the initiative in the portal.

    The policy definitions is an array. Each object in the array is nice and short, with only the policyDefinitionId plus the parameter values required by the definition.

1. Check the syntax for the CLI command

    ```bash
    az policy set-definition create --help
    ```

    Note that name, display name, description and metadata are all switches. Policy definitions can either be JSON strings or JSON files. Parameters are also handled this way.

    This is the reason that many Git repos for policy definitions will have the full definition file (suitable for ARM templates) plus separate files containing only the parameters and policy definitions (for use with CLI commands). We will follow the convention and construct the same.

1. Create a new subdirectory called initiatives

    ```bash
    mkdir -pm 755 initiatives/tags
    cd initiatives/tags
    ```

1. Check the definition for "Add a tag to a resource group"

    We'll be starting with setting the **Owner** tag at the resource group, so check the required parameters for adding a tag to a resource group. We checked this builtin policy in the portal, so we know the resource ID already.

    ```bash
    az policy definition show --name 726aca4c-86e9-4b04-b0c5-073027359532 --output jsonc
    ```

    {{< details "Expected output" >}}
```json
{
  "description": "Adds the specified tag and value when any resource group missing this tag is created or updated. Existing resource groups can be remediated by triggering a remediation task. If the tag exists with a different value it will not be changed.",
  "displayName": "Add a tag to resource groups",
  "id": "/providers/Microsoft.Authorization/policyDefinitions/726aca4c-86e9-4b04-b0c5-073027359532",
  "metadata": {
    "category": "Tags",
    "version": "1.0.0"
  },
  "mode": "All",
  "name": "726aca4c-86e9-4b04-b0c5-073027359532",
  "parameters": {
    "tagName": {
      "allowedValues": null,
      "defaultValue": null,
      "metadata": {
        "additionalProperties": null,
        "description": "Name of the tag, such as 'environment'",
        "displayName": "Tag Name"
      },
      "type": "String"
    },
    "tagValue": {
      "allowedValues": null,
      "defaultValue": null,
      "metadata": {
        "additionalProperties": null,
        "description": "Value of the tag, such as 'production'",
        "displayName": "Tag Value"
      },
      "type": "String"
    }
  },
  "policyRule": {
    "if": {
      "allOf": [
        {
          "equals": "Microsoft.Resources/subscriptions/resourceGroups",
          "field": "type"
        },
        {
          "exists": "false",
          "field": "[concat('tags[', parameters('tagName'), ']')]"
        }
      ]
    },
    "then": {
      "details": {
        "operations": [
          {
            "field": "[concat('tags[', parameters('tagName'), ']')]",
            "operation": "add",
            "value": "[parameters('tagValue')]"
          }
        ],
        "roleDefinitionIds": [
          "/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c"
        ]
      },
      "effect": "modify"
    }
  },
  "policyType": "BuiltIn",
  "type": "Microsoft.Authorization/policyDefinitions"
}
```
{{< /details >}}

    OK, so the policyDefinitionId should be /providers/Microsoft.Authorization/policyDefinitions/726aca4c-86e9-4b04-b0c5-073027359532 and the expected parameters are tagName and tagValue.

1. Create tags.json

    Create a file called tags.json with the following content.

    ```json
    {
        "properties": {
            "displayName": "Azure Citadel Tags",
            "policyType": "Custom",
            "description": "Custom tags: Owner",
            "metadata": {
                "version": "1.0.0",
                "category": "Tags"
            },
            "parameters": {},
            "policyDefinitions": [
                    {
                    "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/726aca4c-86e9-4b04-b0c5-073027359532",
                    "parameters": {
                        "tagName": {
                            "value": "Owner"
                        },
                        "tagValue": {
                            "value": ""
                        }
                    }
                }
            ]
        }
    }
    ```

    Note that the element in the policyDefinitions array specifies the right policyDefinitionId and sets the default value to an empty string.

1. Save the file.

1. Create the tags.definitions.json

    Use jq to create the definitions.json.

    ```bash
    jq .properties.policyDefinitions < tags.json > tags.definitions.json
    ```

## Define, assign and test

OK, we will create the definition and test it out.

1. Create the policy initiative definitions

    Create the definition at the policytest management group.

    ```bash
    az policy set-definition create --name tags \
      --display-name "Azure Citadel Tags" \
      --metadata version="1.0.0" category=Tags \
      --description "Custom tags: Owner" \
      --management-group policytest \
      --definitions tags.definitions.json
    ```

    > If you couldn't create the management group earlier then change the `--management-group` switch to `--subscription <subscription_id>`.

1. Set variables

    Set the policy initiative definition ID and subscriptionScope, plus default location.

    ```text
    policySetDefinitionId=$(az policy set-definition show --management-group policytest --name tags --query id -otsv)
    subscriptionScope=/subscriptions/$(az account show --query id -otsv)
    export AZURE_DEFAULTS_LOCATION=uksouth
    ```

1. Create the assignment

    Create the policy initiative assignment at the subscription scope.

    ```bash
    az policy assignment create --name tags \
      --scope $subscriptionScope \
      --policy-set-definition $policySetDefinitionId \
      --assign-identity --location uksouth\
      --role "Tag Contributor" --identity-scope $subscriptionScope
    ```

    The _Modify_ effect requires a managed identity. Here we are creating a system assigned identity in UK South with Tag Contributor at the subscription scope.

1. Create test resource groups

    ```bash
    az group create --name policytest1 --tags Owner="Richard Cheney"
    az group create --name policytest2
    ```

    Note that the second resource group should still have the Owner tag, but it will be empty.

## Add in additional policies

OK, time to fly solo.

Extend your array of policy definitions to handle setting the following tags at resource group level:

* Department
* Application
* Environment
* Downtime
* Costcode

Don't forget to recreate the tags.definitions.json file.

## Custom policies

There are more BuiltIn policies, but they use the deny effect.  We'll create some Custom policies that use audit instead. I have created a few example policies using the format seen in ARM templates:

{{< details "auditemptytag.json" >}}
```json
{
    "type": "Microsoft.Authorization/policyDefinitions",
    "name": "auditEmptyTagValue",
    "properties": {
        "displayName": "Audit tag exists and has a value",
        "description": "This policy audits that a tag exists and has a non-empty value.",
        "policyType": "Custom",
        "mode": "Indexed",
        "parameters": {
            "tagName": {
                "type": "String",
                "metadata": {
                    "description": "Name of the tag, e.g. 'Environment'",
                    "displayName": "Tag Name"
                }
            }
        },
        "policyRule": {
            "if": {
                "anyOf": [
                    {
                        "exists": "false",
                        "field": "[concat('tags[', parameters('tagName'), ']')]"
                    },
                    {
                        "field": "[concat('tags[', parameters('tagName'), ']')]",
                        "match": ""
                    }
                ]
            },
            "then": {
                "effect": "audit"
            }
        }
    }
}
```
{{< /details >}}

Source: <https://github.com/richeney/arm/blob/master/policies/auditemptytag.json>

{{< details "audittagvalues.json" >}}
```json
{
    "type": "Microsoft.Authorization/policyDefinitions",
    "name": "auditTagValues",
    "properties": {
        "displayName": "Audit tag exists and has a value from the allowedList",
        "description": "This policy audits that a tag exists and has a value from the specified list",
        "policyType": "Custom",
        "mode": "Indexed",
        "parameters": {
            "tagName": {
                "metadata": {
                    "description": "Name of the tag, such as 'costcode'",
                    "displayName": "Tag Name"
                },
                "type": "String"
            },
            "tagValues": {
                "metadata": {
                    "description": "The list of permitted tag values",
                    "displayName": "Permitted Tag Values"
                },
                "type": "Array"
            }
        },
        "policyRule": {
            "if": {
                "anyOf": [
                    {
                        "field": "[concat('tags[', parameters('tagName'), ']')]",
                        "exists": "false"
                    },
                    {
                        "field": "[concat('tags[', parameters('tagName'), ']')]",
                        "notIn": "[parameters('tagValues')]"
                    }
                ]
            },
            "then": {
                "effect": "audit"
            }
        }
    }
}
```
{{< /details >}}

Source: <https://github.com/richeney/arm/blob/master/policies/audittagvalues.json>

{{< details "audittagvaluepattern.json" >}}
```json
{
    "type": "Microsoft.Authorization/policyDefinitions",
    "name": "auditTagValuePattern",
    "properties": {
        "displayName": "Audit tag exists and that the value matches the pattern",
        "description": "This policy audits that a tag exists and has a value that matches the specified pattern",
        "policyType": "Custom",
        "mode": "Indexed",
        "parameters": {
            "tagName": {
                "type": "String",
                "metadata": {
                    "description": "Name of the tag, e.g. 'Costcode'",
                    "displayName": "Tag Name"
                }
            },
            "tagValuePattern": {
                "type": "String",
                "metadata": {
                    "description": "Pattern to use for names. Use ? for characters and # for numbers.",
                    "displayName": "Tag Value Pattern"
                }
            }
        },
        "policyRule": {
            "if": {
                "not": {
                    "field": "[concat('tags.', parameters('tagName'))]",
                    "match": "[parameters('tagValuePattern')]"
                }
            },
            "then": {
                "effect": "audit"
            }
        }
    }
}
```
{{< /details >}}

Source: <https://github.com/richeney/arm/blob/master/policies/audittagvaluepattern.json>

Note how the files contain both the rule and the parameters, as well as the values we have been specifying using the CLI switches. When you look at the policy samples then you'll notice that each sample has a full file for use in ARM template deployments, plus the parameters and rules versions for PowerShell and CLI commands.

## Create the custom policies using addpolicy.sh

The three custom policy files are the full ARM template style JSON so we'll use a utility script, [addpolicy.sh](https://github.com/richeney/arm/blob/master/scripts/addpolicy.sh), to extract the information from it and create the CLI command on the fly.

1. Download the addpolicy.sh script to the current working directory

    ```bash
    curl -sSL https://raw.githubusercontent.com/richeney/arm/master/scripts/addpolicy.sh --output addpolicy.sh && chmod 755 addpolicy.sh
    ```

    > You will need to have the correct RBAC permissions to create policies at the Root Tenant Group to use the script. If you do not have the `Microsoft.Authorization/policyassignments/*` RBAC permission fpr the Root Tenant Group then delete the `--management-group $tenantId \` line from the multi-line `az policy definition create` command.  The custom policies will be created at the subscription scope instead.

1. Use the command without any parameters to display the usage

    ```bash
    ./addpolicy
    ```

1. Add the three custom policies

    If you created the _policytest_ management group then run:

    ```bash
    ./addpolicy.sh -m policytest auditemptytag audittagvalues audittagvaluepattern
    ```

    > If you are having to work at the subscription scope then omit the `-m policytest` switch.

    The script will output the IDs of the resulting policies. Example IDs for the auditemptytag policy:

    | **Scope** | **resourceId** |
    |---|---|
    | Management Group | /providers/Microsoft.Management/managementgroups/policytest/providers/Microsoft.Authorization/policyDefinitions/auditEmptyTagValue |
    | Subscription | /subscriptions/2d31be49-d999-4415-bb65-8aec2c90ba62/providers/Microsoft.Authorization/policyDefinitions/auditEmptyTagValue |

1. List out the custom policies

    In case you need to find out the resourceIds for your custom policies:

    ```bash
    az policy definition list --management-group policytest --query "[? policyType == 'Custom'].id"
    ```

    This should return something similar to:

    ```json
    [
      "/providers/Microsoft.Management/managementgroups/f246eeb7-b820-4971-a083-9e100e084ed0/providers/Microsoft.Authorization/policyDefinitions/auditEmptyTagValue",
      "/providers/Microsoft.Management/managementgroups/f246eeb7-b820-4971-a083-9e100e084ed0/providers/Microsoft.Authorization/policyDefinitions/auditTagValuePattern",
      "/providers/Microsoft.Management/managementgroups/f246eeb7-b820-4971-a083-9e100e084ed0/providers/Microsoft.Authorization/policyDefinitions/auditTagValues"
    ]
    ```

## Custom Policy Initiative

Now, remember that policy initiatives can only use either the BuiltIn policies, or those Custom policies that have been created at the same scope point or higher. It is therefore our recommendation to maintain your own library of parameterised custom policies, and to define those at the highest scope point possible.  You can then customise your policy initiatives per management group or subscription, either through different definitions or via policy initiative parameters.

Now that we have our list of BuiltIn and Custom policies, let's define a policy initiative for tagging that uses a parameter. Once it has been defined then we'll assign it to a lower management group level, specifying the correct Environment value for that scope point.

## Policy initiative definition

1. Create a new subdirectory called initiatives

    ```bash
    mkdir -m 755 initiatives
    cd initiatives
    ```

1. Copy in the following JSON into a new file in the initiatives folder called **tags.definition.json**:

    ```json
    [
        {
            "comment": "Create Owner tag if it does not exist",
            "parameters": {
                "tagName": {
                    "value": "Owner"
                },
                "tagValue": {
                    "value": ""
                }
            },
            "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/2a0e14a6-b0a6-4fab-991a-187a4f81c498"
        },
        {
            "comment": "Audit Owner tag if it is empty",
            "parameters": {
                "tagName": {
                    "value": "Owner"
                }
            },
            "policyDefinitionId": "/providers/Microsoft.Management/managementgroups/f246eeb7-b820-4971-a083-9e100e084ed0/providers/Microsoft.Authorization/policyDefinitions/auditEmptyTagValue"
        },
        {
            "comment": "Create Department tag if it does not exist",
            "parameters": {
                "tagName": {
                    "value": "Department"
                },
                "tagValue": {
                    "value": ""
                }
            },
            "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/2a0e14a6-b0a6-4fab-991a-187a4f81c498"
        },
        {
            "comment": "Check if Department is in the defined list",
            "parameters": {
                "tagName": {
                    "value": "Department"
                },
                "tagValues": {
                    "value": [
                        "Finance",
                        "Human Resources",
                        "Logistics",
                        "Sales",
                        "IT"
                    ]
                }
            },
            "policyDefinitionId": "/providers/Microsoft.Management/managementgroups/f246eeb7-b820-4971-a083-9e100e084ed0/providers/Microsoft.Authorization/policyDefinitions/auditTagValues"
        },
        {
            "comment": "Create Application tag if it does not exist",
            "parameters": {
                "tagName": {
                    "value": "Application"
                },
                "tagValue": {
                    "value": ""
                }
            },
            "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/2a0e14a6-b0a6-4fab-991a-187a4f81c498"
        },
        {
            "comment": "Audit Application tag if it is empty",
            "parameters": {
                "tagName": {
                    "value": "Application"
                }
            },
            "policyDefinitionId": "/providers/Microsoft.Management/managementgroups/f246eeb7-b820-4971-a083-9e100e084ed0/providers/Microsoft.Authorization/policyDefinitions/auditEmptyTagValue"
        },
        {
            "comment": "Create Environment tag with parameters value if it does not exist",
            "parameters": {
                "tagName": {
                    "value": "Environment"
                },
                "tagValue": {
                    "value": "[parameters('Environment')]"
                }
            },
            "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/2a0e14a6-b0a6-4fab-991a-187a4f81c498"
        },
        {
            "comment": "Deny Environment tag if it isn't set to the parameter",
            "parameters": {
                "tagName": {
                    "value": "Environment"
                },
                "tagValue": {
                    "value": "[parameters('Environment')]"
                }
            },
            "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/1e30110a-5ceb-460c-a204-c1c3969c6d62"
        },
        {
            "comment": "Create Downtime tag if it does not exist, with default value",
            "parameters": {
                "tagName": {
                    "value": "Downtime"
                },
                "tagValue": {
                    "value": "Tuesday, 04:00-04:30"
                }
            },
            "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/2a0e14a6-b0a6-4fab-991a-187a4f81c498"
        },
        {
            "comment": "Audit Downtime tag if it is empty",
            "parameters": {
                "tagName": {
                    "value": "Downtime"
                }
            },
            "policyDefinitionId": "/providers/Microsoft.Management/managementgroups/f246eeb7-b820-4971-a083-9e100e084ed0/providers/Microsoft.Authorization/policyDefinitions/auditEmptyTagValue"
        },
        {
            "comment": "Create Costcode tag if it does not exist",
            "parameters": {
                "tagName": {
                    "value": "Costcode"
                },
                "tagValue": {
                    "value": ""
                }
            },
            "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/2a0e14a6-b0a6-4fab-991a-187a4f81c498"
        },
        {
            "comment": "Check that Costcode tag value is a six digit number",
            "parameters": {
                "tagName": {
                    "value": "Costcode"
                },
                "tagValuePattern": {
                    "value": "######"
                }
            },
            "policyDefinitionId": "/providers/Microsoft.Management/managementgroups/f246eeb7-b820-4971-a083-9e100e084ed0/providers/Microsoft.Authorization/policyDefinitions/auditTagValuePattern"
        }
    ]
    ```

    You can see that it is simple to understand, if you know which policyDefinitionIds you are using. Don;t forget to include comments as these will be discarded by the CLI when defining the policy initiative.

    The initiative will force the Environment value to be a specific value, and this has been parameterised. So we need a parameters file for it

1. Create the a **tags.parameters.json** files in the initiatives folder

    ```json
    {
        "Environment": {
            "type": "string",
            "metadata": {
                "description": "Environment, from permitted list",
                "displayName": "Environment"
            },
            "defaultValue": "Prod",
            "allowedValues": [
                "Prod",
                "UAT",
                "Test",
                "Dev"
            ]
        }
    }
    ```

    Note that if you are using an allowedValues list that your defaultValue must be from that list or the definition will not create.

1. Go back up to the parent directory

    ```bash
    cd ..
    ```

1. Create the policy initiative definition

    ```bash
    az policy set-definition create --name tags --display-name "Standard Tags" --description "Tags: Owner, Department, Application, Environment, Downtime, Costcode" --management-group $tenantId --definitions initiatives/tags.definition.json --params initiatives/tags.parameters.json
    ```

    > If you cannot create policy initiative definitions as the Tenant Root Group level then change the scope to another management group, or to the subscription level using the `--subscription` switch. (Note that all of the CLI commands support the `--help` switch, e.g. `az policy set-definition create --help`.)

    Your initiative should be defined with a resourceId similar to the following format:

    ```text
    /providers/Microsoft.Management/managementgroups/f246eeb7-b820-4971-a083-9e100e084ed0/providers/Microsoft.Authorization/policySetDefinitions/tags
    ```

    And you can list out your custom initiatives at the Tenant Root Group using a command similar to the custom policy ine we used earlier in the lab:

    ```bash
    az policy set-definition list --management-group $tenantId --query "[? policyType == 'Custom'].id"
    ```

## Testing the tagging initiative

The initiative has now been defined at the Tenant Root Group. You should still have the Dev management group that you created in lab 4, which had an ID of 230.  We'll eventually assign the initiative there and specify the value of Environment.

But first, let's test it out at a safer resource group level. (It is assumed that you still have a default location set using `az configure --defaults location=westeurope`.)

1. Create the resource group

    ```bash
    az group create --name tagInitiativeTest
    ```

1. Assign the initiative

    ```bash
    initiativeId=/providers/Microsoft.Management/managementgroups/${tenantId}/providers/Microsoft.Authorization/policySetDefinitions/tags
    az policy assignment create --name tags --display-name "Standard Tags" --policy-set-definition $initiativeId --params '{"Environment":{"value": "Dev"}}' --resource-group tagInitiativeTest
    ```

1. Create a test resource

    ```bash
    az disk create --resource-group tagInitiativeTest -n DeleteMe --size-gb 10 --output jsonc
    ```

    The tags in the command output should look like this:

    ```jsonc
      "tags": {
        "Application": "",
        "Costcode": "",
        "Department": "",
        "Downtime": "Tuesday, 04:00-04:30",
        "Environment": "Dev",
        "Owner": ""
      }
    ```

    All of the tags have been created for us.  This is normally preferable to stopping deployment if they are not defined at that point. Downtime is set to the initiative's default value, and Environment is hardcoded to Dev.

1. Check compliancy

    After a period of time, the compliancy with Azure Policy should show the managed disk resource as being out of compliance. Set the scope level to be the tagInitiativeTest resource group within your subscription:

    ![Non Compliant](/policy/images/lab5-noncompliant.png)

    You can also use CLI commands to interrogate the state:

    ```bash
    az policy state list --resource-group tagInitiativeTest --policy-assignment tags --query "[?! isCompliant ].{resourceId:resourceId, policy:policyDefinitionName}" --output table
    ```

1. Update the tags

    Manually update the tags to make the resource compliant for the next compliancy poll

    ![Updated Tags](/policy/images/lab5-updatedtags.png)

1. Use REST API call to trigger evaluation

    The Azure docs have information on the standard [evaluation triggers](https://docs.microsoft.com/en-us/azure/governance/policy/how-to/get-compliance-data#evaluation-triggers), but I'm going to assume that you're impatient and don't want to wait 24 hours for the standard compliance evaluation cycle to go round.

    We'll use the REST API to trigger an [on demand scan](https://docs.microsoft.com/en-us/azure/governance/policy/how-to/get-compliance-data#on-demand-evaluation-scan) on our resource group. (Hopefully this will become a CLI command in the future.)

    ```bash
    subscriptionId=$(az account show --output tsv --query id)
    rg=tagInitiativeTest
    triggeruri=https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$rg/providers/Microsoft.PolicyInsights/policyStates/latest/triggerEvaluation?api-version=2018-07-01-preview

    accessToken=$(az account get-access-token --output tsv --query accessToken)
    curl --silent --include --header "Authorization: Bearer $accessToken" --header "Content-Type: application/json" --data '{}' --request POST $triggeruri
    ```

    You should get back a number of header from the HTTP 202 response, including a location URI.

1. Wait for evaluation completion

    You can track the scanning event in the Activity Log if you have non-compliant resources:

    * Azure Policy
    * Click on the Standard Tags policy assigned to the test resource group scope
    * Click on Non-compliant Resources
    * Click on the ellipsis (**...**) to the right of the non-compliant resource
    * Click on Show Activity Logs

    The _Trigger Policy Insights Compliance Evaluation_ operation will show a status of 'Accepted', which will become 'Started' and then 'Succeeded' once the policy evaluation has finished.

1. \[Optional] - Query the location URI for scanning status

    Alternatively, you can use REST You can use a REST API GET command against that location URI.

    The REST call returns **202 Accepted** whilst running and then **200 OK** once the evaluation has completed.

    Here is an example of the REST call against the location URI:

    ```bash
    locationuri=https://management.azure.com/subscriptions/2d31be49-d959-4415-bb65-8aec2c90ba62/providers/Microsoft.PolicyInsights/asyncOperationResults/eyJpZCI6IlBTUkFKb2I6MkRFMTVERjczMSIsImxvY2F0aW9uIjoiIn0?api-version=2018-07-01-preview

    curl --silent --include --header "Authorization: Bearer $accessToken" --header "Content-Type: application/json" --request GET $locationuri
    ```

    > Note that your location URI will not be the same as the one above. Use the `location:` value in the trigger output.

1. Verify compliancy

    Once the resources have been rescanned, you can return to the Azure Policy screen and verify that the resource is now compliant with the Default Tagging policy initiative.

    ![Compliant](/policy/images/lab5-compliant.png)

## Assigning the policy initiative to a management group

1. Remove the test resource group

    Deleting the test resource group will delete the resource group, the resources and the policy assignment.

    ```bash
    az group delete --yes --no-wait --name tagInitiativeTest
    ```

1. Add the policy to a higher scope point

    Now that you have tested the policy, you can assign it at a more appropriate scope point, such as the Dev management group.

    ```bash
    tenantId=$(az account show --query tenantId --output tsv)
    initiatives=/providers/Microsoft.Management/managementgroups/${tenantId}/providers/Microsoft.Authorization/policySetDefinitions
    mgs=/providers/Microsoft.Management/managementGroups
    az policy assignment create --name tags --display-name "Standard Tags" --policy-set-definition $initiatives/tags --params '{"Environment":{"value": "Dev"}}' --scope $mgs/230
    ```

## Resources

The following resources are useful. If you want to manage custom policies effectively then make sure you run through the GitHub policy as code tutorial

* <https://docs.microsoft.com/azure/governance/policy/tutorials/govern-tags>
* <https://docs.microsoft.com/azure/governance/policy/concepts/effects#modify>
* <https://docs.microsoft.com/azure/governance/policy/concepts/policy-as-code>
* <https://docs.microsoft.com/azure/governance/policy/tutorials/policy-as-code-github>

## Wrapping up

OK, that is quite a few labs covering the creation of policies and initiatives, and how they work with management groups.

In the next lab we will explore an example workflow for deploying a new tenancy in Azure, using Terraform to create the management groups, policy definitions, assignments and RBAC assignments.