diff --git a/demos/1-PowerShellGet.ps1 b/demos/1-PowerShellGet.ps1
index 8a3a704953..769fa6f449 100644
--- a/demos/1-PowerShellGet.ps1
+++ b/demos/1-PowerShellGet.ps1
@@ -4,11 +4,13 @@
Get-PackageProvider #(should show 2 providers - NuGet,PowerShellGet)
## Using PowerShellGet find and install other demos
+# Value: equivalent of pypi
+# Look for all the modules we'll be demoing today
+Find-Module -Tag 'Open PowerShell','Demos'
+# Pipe this to Install-Module to install them
Find-Module -Tag 'Open PowerShell','Demos' | Install-Module -Verbose
Get-Module
-
-# Find and Download node.js from nuget.org
-Find-Package -Name node.js -ProviderName NuGet -Verbose -Source http://nuget.org/api/v2
+# Note that ScriptAnalyzer gets installed because the VSCode demo is dependent upon it
# Register trusted endpoints
Register-PackageSource -Name NuGet -Location http://nuget.org/api/v2 -Trusted -ProviderName NuGet
diff --git a/demos/apache.ps1 b/demos/apache.ps1
new file mode 100644
index 0000000000..e9f3cd2bcf
--- /dev/null
+++ b/demos/apache.ps1
@@ -0,0 +1,14 @@
+# Get-ApacheVHosts
+# Enumerate configured virtual hosts (web sites) on an apache server
+
+# New-ApacheVHost
+# Given defined parameters, create a $ServerName.conf virtual host config file at $VHostsDirectory
+
+# Get-ApacheModules
+# List installed Apache Modules
+
+# Demo execution:
+# Run Get-ApacheVHosts and enumerate configured virtual hosts
+# Run Get-ApacheModules to show installed Apache modules
+# Run New-ApacheVHost to create a new virtual host and restart Apache
+# Run Get-ApacheVhosts to show the newly created virtual host
diff --git a/demos/crontab.ps1 b/demos/crontab.ps1
new file mode 100644
index 0000000000..d90e2dbdb8
--- /dev/null
+++ b/demos/crontab.ps1
@@ -0,0 +1,9 @@
+# Use the crontab module available at ./modules/CronTab
+
+# Get the existing cron jobs
+
+# Sort them by data
+
+# Create a new one using a DateTime object
+
+# Show in bash that the new cron job exists
diff --git a/demos/dsc.ps1 b/demos/dsc.ps1
new file mode 100644
index 0000000000..c1fdd69037
--- /dev/null
+++ b/demos/dsc.ps1
@@ -0,0 +1,11 @@
+# DSC MOF Compilation
+# DSC Configuration() script that:
+# Defines base configuration users, groups, settings
+# Uses PS function to set package configuration (ensure=Present) for an array of packages
+# Probes for the existence of a package (Apache or MySQL) and conditionally configures the workload. I.e., if Apache is installed, configure Apache settings
+
+# Demo execution:
+# Show the .ps1
+# Run the .ps1 to generate a MOF
+# Apply the MOF locally with Start-DSCConfiguration
+# Show the newly configured state
\ No newline at end of file
diff --git a/demos/json.ps1 b/demos/json.ps1
new file mode 100644
index 0000000000..4804ffc699
--- /dev/null
+++ b/demos/json.ps1
@@ -0,0 +1,11 @@
+# Get-Mongostat
+# Get mongodb statistics with parsed JSON
+
+# journalctl is a native tool to emit SystemD's journal, which includes diagnostic info pertaining to SystemD services
+# Show-JournalCtl
+# Given journalctl parameters, run journalctl with a -o json to emit JSON, e.g. journalctl $Parameters -o json --no-pager
+# Return structured data from Journal
+
+# Demo execution
+# Show journatcl example
+# Using Where & Sort, filter journal output to drill into a specific problem (e.g. service failed to start)
\ No newline at end of file
diff --git a/demos/modules/ApacheHTTPServer/ApacheVirtualHosts.psm1 b/demos/modules/ApacheHTTPServer/ApacheVirtualHosts.psm1
new file mode 100644
index 0000000000..94710c8130
--- /dev/null
+++ b/demos/modules/ApacheHTTPServer/ApacheVirtualHosts.psm1
@@ -0,0 +1,173 @@
+$moduleRoot = Split-Path -Path $MyInvocation.MyCommand.Path
+
+
+Function New-ApacheVHost {
+ [CmdletBinding()]
+ param (
+ [parameter (Mandatory = $true)][string]$ServerName,
+ [string]$VHostsDirectory,
+ [parameter (Mandatory = $true)][string]$DocumentRoot,
+ [parameter (Mandatory = $true)][string]$VirtualHostIPAddress,
+ [string[]]$ServerAliases,
+ [parameter (Mandatory = $true)][string]$VirtualHostPort,
+ [string]$ServerAdmin,
+ [string]$CustomLogPath,
+ [string]$ErrorLogPath
+ )
+
+ if ($VHostsDirectory -notlike "*/*"){
+ #Try to find default
+ if (Test-Path "/etc/httpd/conf.d"){
+ $VHostsDirectory = "/etc/httpd/conf.d/"
+ }
+ if (Test-Path "/etc/apache2/sites-enabled"){
+ $VHostsDirectory = "/etc/apache2/sites-enabled"
+ }
+ }
+
+ if (!(Test-Path $VHostsDirectory)){
+ Write-Error "Specified virtual hosts directory does not exist: $VHostsDirectory"
+ exit 1
+ }
+
+ $vHostDef = ""
+
+ $vHostDef += "`n"
+ $vHostDef += "DocumentRoot " + $DocumentRoot + "`n"
+ $vHostDef += "ServerName " + $ServerName + "`n"
+ if ($ServerAliases -ne $null){
+ ForEach ($Alias in $ServerAliases){
+ $vHostDef += "ServerAlias " + $Alias + "`n"
+ }
+ }
+ if ($ServerAdmin.Length -gt 1){
+ $vHostDef += "ServerAdmin " + $ServerAdmin +"`n"
+ }
+ if ($CustomLogPath -like "*/*"){
+ $vHostDef += "CustomLog " + $CustomLogPath +"`n"
+ }
+ if ($ErrorLogPath -like "*/*"){
+ $vHostDef += "ErrorLog " + $ErrorLogpath +"`n"
+ }
+ $vHostDef += ""
+ $filName = $ServerName + ".conf"
+ Write-Output "Writing $filName to $VHostsDirectory"
+ $VhostDef |out-file "$VHostsDirectory/$filName" -Force -Encoding:ascii
+
+ if (Test-Path "/usr/sbin/apache2ctl"){
+ $cmd = "/usr/sbin/apache2ctl"
+ }elseif(Test-Path "/usr/sbin/httpd"){
+ $cmd = "/usr/sbin/httpd"
+ }
+ $cmd += " -k restart"
+ write-output "Restarting Apache HTTP daemon"
+ Invoke-expression $cmd
+}
+
+Function Get-ApacheVHosts{
+ $cmd = $null
+ if (Test-Path "/usr/sbin/apache2ctl"){
+ $cmd = "/usr/sbin/apache2ctl"
+ }elseif(Test-Path "/usr/sbin/httpd"){
+ $cmd = "/usr/sbin/httpd"
+ }
+
+
+ function New-VHostObj {
+ New-Object PSObject -Property @{}
+ }
+
+
+
+ if ($cmd -eq $null){
+ Write-Error "Unable to find httpd/apache2ctl command"
+ exit -1
+ }
+
+ $cmd = $cmd += " -t -D DUMP_VHOSTS"
+ $Vhosts = @()
+ $res = Invoke-expression $cmd
+ ForEach ($line in $res){
+ if ($line -like "*.conf*"){
+ $vhobject = New-VHostObj
+ $RMatch = $line -match "(?.*:[0-9]*)(?.*)\((?.*)\)"
+ $VHostProps = $matches
+ $vhobject |Add-Member -type NoteProperty -name "ServerName" -value $matches.ServerName.Trim()
+ $vhobject |Add-Member -type NoteProperty -name "ListenAddress" -value $matches.Listen.Trim()
+ $vhobject |Add-Member -type NoteProperty -name "ConfFile" -value $matches.ConfFile.Trim()
+ $Vhosts += $vhobject
+ }
+
+ }
+
+ Return $Vhosts
+
+}
+
+Function Restart-ApacheHTTPServer{
+ [CmdletBinding()]
+ Param(
+ [boolean]$Graceful
+ )
+
+ if ($Graceful -eq $null){$Graceful = $fase}
+
+ $cmd = $null
+ if (Test-Path "/usr/sbin/apache2ctl"){
+ $cmd = "/usr/sbin/apache2ctl"
+ }elseif(Test-Path "/usr/sbin/httpd"){
+ $cmd = "/usr/sbin/httpd"
+ }
+
+ if ($cmd -eq $null){
+ Write-Error "Unable to find httpd/apache2ctl command"
+ exit -1
+ }
+
+ if ($Graceful){
+ $cmd = $cmd += " -k graceful"
+ }else{
+ $cmd = $cmd += " -k restart"
+ }
+
+ Invoke-Expression $Cmd
+}
+
+Function Get-ApacheModules{
+ $cmd = $null
+ if (Test-Path "/usr/sbin/apache2ctl"){
+ $cmd = "/usr/sbin/apache2ctl"
+ }elseif(Test-Path "/usr/sbin/httpd"){
+ $cmd = "/usr/sbin/httpd"
+ }
+
+ if ($cmd -eq $null){
+ Write-Error "Unable to find httpd/apache2ctl command"
+ exit -1
+ }
+
+ $cmd = $cmd += " -M |grep -v Loaded"
+
+ $ApacheModules = @()
+ function New-ModuleObj {
+ New-Object PSObject -Property @{}
+ }
+
+
+ $Results = Invoke-Expression $cmd
+ Foreach ($mod in $Results){
+ $modObj = New-ModuleObj
+ $modObj | Add-Member -type NoteProperty -name "Module" -value $mod.trim()
+ $ApacheModules += $modObj
+ }
+
+ Return $ApacheModules
+
+}
diff --git a/demos/modules/CronTab/CronTab.ps1xml b/demos/modules/CronTab/CronTab.ps1xml
new file mode 100644
index 0000000000..1d1f1f90d8
--- /dev/null
+++ b/demos/modules/CronTab/CronTab.ps1xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+ Default
+
+ CronJob
+
+
+
+
+
+ 10
+ Left
+
+
+
+ 10
+ Left
+
+
+
+ 10
+ Left
+
+
+
+ 10
+ Left
+
+
+
+ 10
+ Left
+
+
+
+ Left
+
+
+
+
+
+
+ Minute
+
+
+ Hour
+
+
+ DayOfMonth
+
+
+ Month
+
+
+ DayOfWeek
+
+
+ Command
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/demos/modules/CronTab/CronTab.psd1 b/demos/modules/CronTab/CronTab.psd1
new file mode 100644
index 0000000000..23d9d95f05
Binary files /dev/null and b/demos/modules/CronTab/CronTab.psd1 differ
diff --git a/demos/modules/CronTab/CronTab.psm1 b/demos/modules/CronTab/CronTab.psm1
new file mode 100644
index 0000000000..f8bd2e7c4f
--- /dev/null
+++ b/demos/modules/CronTab/CronTab.psm1
@@ -0,0 +1,189 @@
+$crontabcmd = "/usr/bin/crontab"
+
+#TODO fix after https://github.com/PowerShell/PowerShell/issues/932 is fixed
+#class CronJob {
+# [string] $Minute
+# [string] $Hour
+# [string] $DayOfMonth
+# [string] $Month
+# [string] $DayOfWeek
+# [string] $Command
+#}
+
+# Internal helper functions
+
+function Get-CronTab ([String] $user) {
+ $crontab = Invoke-CronTab -user $user -arguments "-l" -noThrow
+ if ($crontab -is [System.Management.Automation.ErrorRecord]) {
+ if ($crontab.Exception.Message.StartsWith("no crontab for ")) {
+ $crontab = $null
+ }
+ else {
+ throw $crontab.Exception
+ }
+ }
+ [string[]] $crontab
+}
+
+function ConvertTo-CronJob ([String] $crontab) {
+ $split = $crontab.split(" ", 6)
+ $cronjob = New-Object -TypeName PSObject -Property @{ #TODO: change to CronJob type
+ PSTypeName="CronJob";
+ Minute = $split[0];
+ Hour = $split[1];
+ DayOfMonth= $split[2];
+ Month =$split[3];
+ DayOfWeek = $split[4];
+ Command = $split[5]
+ }
+ $cronjob
+}
+
+function Invoke-CronTab ([String] $user, [String] $arguments, [Switch] $noThrow) {
+ If ($user -ne [String]::Empty) {
+ $arguments = "-u $UserName $arguments"
+ }
+
+ $cmd = "$crontabcmd $arguments 2>&1"
+ Write-Verbose $cmd
+ $output = Invoke-Expression $cmd
+ if ($LastExitCode -ne 0 -and -not $noThrow) {
+ $e = New-Object System.InvalidOperationException -ArgumentList $output.Exception.Message
+ throw $e
+ } else {
+ $output
+ }
+}
+
+function Import-CronTab ([String] $user, [String[]] $crontab) {
+ $temp = New-TemporaryFile
+ [String]::Join([Environment]::NewLine,$crontab) | Set-Content $temp.FullName
+ Invoke-CronTab -user $user $temp.FullName
+ Remove-Item $temp
+}
+
+# Public functions
+
+function Remove-CronJob {
+<#
+.SYNOPSIS
+ Removes the exactly matching cron job from the cron table
+.DESCRIPTION
+ Removes the exactly matching cron job from the cron table
+.EXAMPLE
+ Get-CronJob | ? {%_.Command -like 'foo *'} | Remove-CronJob
+.RETURNVALUE
+ None
+.PARAMETER UserName
+ Optional parameter to specify a specific user's cron table
+.PARAMETER Job
+ Cron job object returned from Get-CronJob
+#>
+ [CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact="High")]
+ param (
+ [Alias("u")][Parameter(Mandatory=$false)][String] $UserName,
+ [Alias("j")][Parameter(Mandatory=$true,ValueFromPipeline=$true)][PSTypeName("CronJob")] $Job #TODO use CronJob type
+ )
+ process {
+
+ [string[]] $crontab = Get-CronTab -user $UserName
+ [string[]] $newcrontab = $null
+ $found = $false
+
+ foreach ($line in $crontab) {
+ $cronjob = ConvertTo-CronJob -crontab $line
+ if ((Compare-object $cronjob.psobject.properties $Job.psobject.properties) -eq $null) {
+ $found = $true
+ } else {
+ $newcrontab += $line
+ }
+ }
+
+ if (-not $found) {
+ $e = New-Object System.Exception -ArgumentList "Job not found"
+ throw $e
+ }
+ if ($pscmdlet.ShouldProcess($Job.Command,"Remove")) {
+ Import-CronTab -user $UserName -crontab $newcrontab
+ }
+ }
+}
+
+function New-CronJob {
+<#
+.SYNOPSIS
+ Create a new cron job
+.DESCRIPTION
+ Create a new job in the cron table. Date and time parameters can be specified
+ as ranges such as 10-30, as a list: 5,6,7, or combined 1-5,10-15. An asterisk
+ means 'first through last' (the entire allowed range). Step values can be used
+ with ranges or with an asterisk. Every 2 hours can be specified as either
+ 0-23/2 or */2.
+.EXAMPLE
+ New-CronJob -Minute 10-30 -Hour 10-20/2 -DayOfMonth */2 -Command "/bin/bash -c 'echo hello' > ~/hello"
+.RETURNVALUE
+ If successful, an object representing the cron job is returned
+.PARAMETER UserName
+ Optional parameter to specify a specific user's cron table
+.PARAMETER Minute
+ Valid values are 0 to 59. If not specified, defaults to *.
+.PARAMETER Hour
+ Valid values are 0-23. If not specified, defaults to *.
+.PARAMETER DayOfMonth
+ Valid values are 1-31. If not specified, defaults to *.
+.PARAMETER Month
+ Valid values are 1-12. If not specified, defaults to *.
+.PARAMETER DayOfWeek
+ Valid values are 0-7. 0 and 7 are both Sunday. If not specified, defaults to *.
+.PARAMETER Command
+ Command to execute at the scheduled time and day.
+#>
+ [CmdletBinding()]
+ param (
+ [Alias("u")][Parameter(Mandatory=$false)][String] $UserName,
+ [Alias("mi")][Parameter(Position=1)][String[]] $Minute = "*",
+ [Alias("h")][Parameter(Position=2)][String[]] $Hour = "*",
+ [Alias("dm")][Parameter(Position=3)][String[]] $DayOfMonth = "*",
+ [Alias("mo")][Parameter(Position=4)][String[]] $Month = "*",
+ [Alias("dw")][Parameter(Position=5)][String[]] $DayOfWeek = "*",
+ [Alias("c")][Parameter(Mandatory=$true,Position=6)][String] $Command
+ )
+ process {
+ # TODO: validate parameters, note that different versions of crontab support different capabilities
+ $line = "{0} {1} {2} {3} {4} {5}" -f [String]::Join(",",$Minute), [String]::Join(",",$Hour),
+ [String]::Join(",",$DayOfMonth), [String]::Join(",",$Month), [String]::Join(",",$DayOfWeek), $Command
+ [string[]] $crontab = Get-CronTab -user $UserName
+ $crontab += $line
+ Import-CronTab -User $UserName -crontab $crontab
+ ConvertTo-CronJob -crontab $line
+ }
+}
+
+function Get-CronJob {
+<#
+.SYNOPSIS
+ Returns the current cron jobs from the cron table
+.DESCRIPTION
+ Returns the current cron jobs from the cron table
+.EXAMPLE
+ Get-CronJob -UserName Steve
+.RETURNVALUE
+ CronJob objects
+.PARAMETER UserName
+ Optional parameter to specify a specific user's cron table
+#>
+ [CmdletBinding()]
+ [OutputType([PSObject])]
+ param (
+ [Alias("u")][Parameter(Mandatory=$false)][String] $UserName
+ )
+ process {
+ $crontab = Get-CronTab -user $UserName
+ ForEach ($line in $crontab) {
+ if ($line.Trim().Length -gt 0)
+ {
+ ConvertTo-CronJob -crontab $line
+ }
+ }
+ }
+}
diff --git a/demos/modules/README.md b/demos/modules/README.md
new file mode 100644
index 0000000000..9a7f330f3a
--- /dev/null
+++ b/demos/modules/README.md
@@ -0,0 +1 @@
+# Samples
diff --git a/demos/remoting.ps1 b/demos/remoting.ps1
new file mode 100644
index 0000000000..fe909db1b9
--- /dev/null
+++ b/demos/remoting.ps1
@@ -0,0 +1,2 @@
+# Show PSRP working over OpenSSH
+# Includes Windows -> Linux and Linux -> Windows
\ No newline at end of file
diff --git a/demos/rest.ps1 b/demos/rest.ps1
new file mode 100644
index 0000000000..a7b8c799fd
--- /dev/null
+++ b/demos/rest.ps1
@@ -0,0 +1,7 @@
+# Get all repositories from the GitHub API, sort by most pull requests, most stars, most forks, etc.
+
+# Get repo info (json) from the github api
+# Convert json to PS object
+# Set the private status to false
+# Convert back to json
+# Push the new json back to the github api
\ No newline at end of file
diff --git a/demos/xml.ps1 b/demos/xml.ps1
new file mode 100644
index 0000000000..151ee628fb
--- /dev/null
+++ b/demos/xml.ps1
@@ -0,0 +1,42 @@
+Dev: XML legacy [ETS?]
+What are some xml workloads we can simplify with XML parsing?
+Maybe cracking open some repository metadata?
+JEE servers use XML pretty extensively for configuration. Perhaps a script to adjust JEE server settings? Obviously, parsing XML in bash is fraught with danger...
+
+PS /etc/tomcat> $tomcatserverfile="/usr/share/tomcat/conf/server.xml"
+PS /etc/tomcat> [xml]$tomcatconfig=Get-Content $tomcatserverfile
+PS /etc/tomcat> $tomcatconfig.Server
+
+
+port : 8005
+shutdown : SHUTDOWN
+#comment : { Security listener. Documentation at /docs/config/listeners.html
+
+ , APR library loader. Documentation at /docs/apr.html , Initialize Jasper prior to webapps are loaded. Documentation at
+ /docs/jasper-howto.html , Prevent memory leaks due to use of particular java/javax APIs...}
+Listener : {Listener, Listener, Listener, Listener...}
+GlobalNamingResources : GlobalNamingResources
+Service : Service
+
+
+
+
+PS /etc/tomcat> $tomcatconfig.Server.port
+8005
+PS /etc/tomcat> $tomcatconfig.Server.Listener
+
+className SSLEngine
+--------- ---------
+org.apache.catalina.core.AprLifecycleListener on
+org.apache.catalina.core.JasperListener
+org.apache.catalina.core.JreMemoryLeakPreventionListener
+org.apache.catalina.mbeans.GlobalResourcesLifecycleListener
+org.apache.catalina.core.ThreadLocalLeakPreventionListener
+
+
+PS /etc/tomcat> $tomcatconfig.Server.Service
+
+name #comment
+---- --------
+Catalina {The connectors can use a shared executor, you can define one or more named thread pools, ...
+