Building cross-platform application with Powershell and Apache CouchDB
Tags: powershell, nosql, database, shell
Published: 2019-08-01 15:11:16

# CouchDB Database
[Apache CouchDB](http://couchdb.apache.org/) is a no-sql database, implemented on the HTTP and JSON API.

## Advantage
This type of database is structured in a `database -> documents -> revision_document -> data`. This allows for versioned documents. But it is not the only point of advantage; the replication in CouchDB is really the killer feature that sets it apart from other databases.

# (Power)Shell
[Powershell](https://docs.microsoft.com/en-us/powershell/) is a shell developed in .NET core, and is a shell with typed objects. PowerShell promises to revolutionize the world of system management and
command-line shells. From its object-based pipelines to its administrator focus to its enormous reach into other Microsoft management technologies, PowerShell drastically improves the productivity of administrators and power users alike.

## PSCouchDB module
To interface with CouchDB, you need to install a powershell module: [PSCouchDB](https://matteoguadrini.github.io/PSCouchDB/).

Installation is simple; or use git ([documentation](https://pscouchdb.readthedocs.io/en/latest/intro.html#installation)):
```powershell
git clone https://github.com/MatteoGuadrini/PSCouchDB.git
cd PSCouchDB
# for Windows
copy /Y PSCouchDB %Windir%\System32\WindowsPowerShell\v1.0\Modules
# for Unix
cp -var PSCouchDB /usr/local/share/powershell/Modules
```
or [PowershellGallery](https://www.powershellgallery.com/packages/PSCouchDB):
```powershell
Install-Module -Name PSCouchDB 
```

### Test pscouchdb module
With this, you can discover all cmdlets available to work with CouchDB in PSCouchDB module.

```powershell
Search-CouchDBHelp -Pattern .
```

### Demonstration
[![asciicast](https://asciinema.org/a/232696.svg)](https://asciinema.org/a/232696)

# Configure CouchDB cluster
The time has come to configure our database. We begin to decide which type of cluster will be our database.
For a simple configuration, as in our case, we choose a single node cluster.

## Admin
But first, you need to create an admin user who can write and read from the database. (Without an admin, anyone can write and read from various databases; refer to the [Admin Party](https://docs.couchdb.org/en/stable/intro/security.html#the-admin-party) guide.)
Create a *password* and *admin* user:
 
```powershell
$pwd = "password" | ConvertTo-SecureString -AsPlainText -Force
New-CouchDBAdmin -Userid admin -Password $pwd
```
Test admin user has been created:
```powershell
Get-CouchDBAdmin -Authorization admin:password
```
## Enable cluster
Now, enable our single cluster mode:
```powershell
Enable-CouchDBCluster -SingleNode -Authorization admin:password
```
Verify that cluster has been created, verifying that he created the three databases: **_global_changes**, **_replicator**, **_users**
```powershell
Get-CouchDBDatabase
```
## Create a database for test
We create a test database to verify that everything is working properly:
```powershell
New-CouchDBDatabase -Database test -Authorization admin:password
```

# Create application
Applications in general solve a real problem. In this case, let us imagine that we must survey the hardware of our company. We need an application that every few minutes takes information from clients on Active Director and puts it in a database.

## Simple machine inventory

### Create a database
Let's start by creating the database that will host the machine information:
```powershell
New-CouchDBDatabase -Database hw_inventory -Authorization admin:password
```

### Create a get function
Let's start with editing the file called *HardwareInventory.ps1*, with this function:

```powershell
function Get-ComputerInventory ($Authorization) {
    # Create an Active Directory session
    $session = New-PSSession -ComputerName "your_domain_controller.local"
    Import-PSSession -Session $session -module ActiveDirectory
    # Get all computer
    $AllComputers = Get-ADComputer -Filter * -Properties DNSHostName
    foreach ($ComputerName in $AllComputers.DNSHostName) {
        $info = @{}
        # Test connection of computer
        if (Test-Connection $ComputerName -Count 1 -Quiet) {
            # Select various info
            $info.Add('ComputerHW', (Get-CimInstance -Class Win32_ComputerSystem -ComputerName $ComputerName |
            select Manufacturer,
                   Model,
                   NumberOfProcessors,
                   @{Expression={$_.TotalPhysicalMemory / 1GB};Label="TotalPhysicalMemoryGB"}))
            $info.Add('ComputerCPU', (Get-CimInstance win32_processor -ComputerName $ComputerName |
            select DeviceID,
                   Name,
                   Manufacturer,
                   NumberOfCores,
                   NumberOfLogicalProcessors))
            $info.Add('ComputerDisks', (Get-CimInstance -Class Win32_LogicalDisk -Filter "DriveType=3" -ComputerName $ComputerName |
            select DeviceID,
                   VolumeName,
                   @{Expression={$_.Size / 1GB};Label="SizeGB"}))
            $info.Add("timestamp", (Get-Date -f MM-dd-yyyy_HH_mm_ss))
            # Write on database
            if (Get-CouchDBDocument -Database hw_inventory -Document $ComputerName -ErrorAction SilentlyContinue -Authorization $Authorization) {
                Set-CouchDBDocument -Database hw_inventory -Document $ComputerName -Data $info -Revision $(Get-CouchDBDocument -Database hw_inventory -Document $ComputerName)._rev -Replace
            } else {
                New-CouchDBDocument -Database hw_inventory -Document $ComputerName -Data $info
            }
        }
    }
    Get-PSSession | Remove-PSSession
}
```

With this function, we can take the basic information of each machine in our domain via WMI:

```
$info = @{}
$info.Add('ComputerHW', (Get-CimInstance -Class Win32_ComputerSystem -ComputerName $ComputerName |
            select Manufacturer,
                   Model,
                   NumberOfProcessors,
                   @{Expression={$_.TotalPhysicalMemory / 1GB};Label="TotalPhysicalMemoryGB"}))
$info.Add('ComputerCPU', (Get-CimInstance win32_processor -ComputerName $ComputerName |
            select DeviceID,
                   Name,
                   Manufacturer,
                   NumberOfCores,
                   NumberOfLogicalProcessors))
$info.Add('ComputerDisks', (Get-CimInstance -Class Win32_LogicalDisk -Filter "DriveType=3" -ComputerName $ComputerName |
            select DeviceID,
                   VolumeName,
                   @{Expression={$_.Size / 1GB};Label="SizeGB"}))
$info.Add("timestamp", (Get-Date -f MM-dd-yyyy_HH_mm_ss))
```

and write in database:
```
# Write on database
if (Get-CouchDBDocument -Database hw_inventory -Document $ComputerName -ErrorAction SilentlyContinue -Authorization $Authorization) {
    Set-CouchDBDocument -Database hw_inventory -Document $ComputerName -Data $info -Revision $(Get-CouchDBDocument -Database hw_inventory -Document $ComputerName)._rev -Replace
} else {
    New-CouchDBDocument -Database hw_inventory -Document $ComputerName -Data $info
}
```

### Create a find function
Once we memorize the data, we would also need to look for them in some way; then we have to create a function always inside our file *HardwareInventory.ps1* that can search the database:
```powershell
# Find computer into inventory
function Find-ComputerInventory () {
    param(
        [Parameter(mandatory=$true)]
        [string] $Computername
    )
    $docs = Find-CouchDBDocuments -Database hw_inventory -Selector "_id" -Operator regex -Value ".*$Computername.*" -Fields _id,ComputerHW,ComputerCPU,ComputerDisks,timestamp
    $docs.docs
}
```
This function allows you to search for the name of the machine and extract the data we are interested in.

### Prepare functions
At this point, we save and include our file in the *profile.ps1* of all users of the machine:
```powershell
". path_of_file/Hardware.ps1" >> $PsHome\Profile.ps1
```

### Use and schedule functions
Now it's time to schedule everything every hour in order to collect the first data, with this command:
```powershell
# if use a powershell 6 or high, use pwsh
powershell -Command "Get-ComputerInventory -Authorization admin:password"
```

### Find a machine
Let's wait a couple of hours and look for a car to see if everything is working properly:
```powershell
Find-ComputerInventory -ComputerName machine1
```

# Conclusion
In 10 minutes we created a machine inventory application in powershell that interfaces to CouchDB, in complete safety.

# References
[Powershell docs](https://docs.microsoft.com/en-us/powershell/scripting/overview?view=powershell-6)
[CouchDB docs](https://docs.couchdb.org/en/stable/)
[PSCouchDB docs](https://pscouchdb.readthedocs.io/en/latest/)
[PSCouchDB github](https://github.com/MatteoGuadrini/PSCouchDB)

[Donations](https://pscouchdb.readthedocs.io/en/latest/support.html#donations)