Skip to content

Commit

Permalink
Add new infrastructure and script files for user management and VM ac…
Browse files Browse the repository at this point in the history
…cess

- Create GitHub Actions workflow for validating PowerShell scripts using PSScriptAnalyzer.
- Add Heat templates for deploying Windows and Linux instances with networking configurations.
- Implement PowerShell script to generate a CSV of Active Directory users with random credentials.
- Create Bash scripts for accessing VMs via RDP on Linux and macOS.
- Introduce a new YAML template for deploying a single Ubuntu instance.
- Update existing YAML templates for Windows Server and Windows 10 instances with floating IPs.
  • Loading branch information
erikhje committed Dec 29, 2025
1 parent 84fe6e3 commit 2f59d14
Show file tree
Hide file tree
Showing 11 changed files with 1,046 additions and 0 deletions.
30 changes: 30 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# .github/workflows/validate.yml
name: Validate PowerShell Scripts

on:
push:
paths:
- "scripts/**/*.ps1"
- ".github/workflows/validate.yml"
pull_request:
paths:
- "scripts/**/*.ps1"
- ".github/workflows/validate.yml"

jobs:
validate:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install PSScriptAnalyzer
shell: pwsh
run: |
Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted
Install-Module PSScriptAnalyzer -Scope CurrentUser -Force -SkipPublisherCheck
- name: Run ScriptAnalyzer
shell: pwsh
run: |
Invoke-ScriptAnalyzer -EnableExit scripts/*.ps1
211 changes: 211 additions & 0 deletions cl_dc_srv_basic.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
heat_template_version: 2013-05-23

description: >
HOT template to create a new neutron network plus a router to the public
network, and for deploying one Windows 10 (hostname cl1) and two Windows
Servers (hostnames dc1 and srv1) without any configuration (only cl1 has
a boot script to set correct hostname).
parameters:
key_name:
type: string
description: Name of keypair to assign to servers

resources:
private_net:
type: OS::Neutron::Net

private_subnet:
type: OS::Neutron::Subnet
properties:
network_id: { get_resource: private_net }
cidr: 192.168.111.0/24
gateway_ip: 192.168.111.1
allocation_pools:
- start: 192.168.111.101
end: 192.168.111.200

router:
type: OS::Neutron::Router
properties:
external_gateway_info:
network: ntnu-internal

router_interface:
type: OS::Neutron::RouterInterface
properties:
router_id: { get_resource: router }
subnet_id: { get_resource: private_subnet }

sec_core:
type: OS::Neutron::SecurityGroup
properties:
description: Security group rules for all
name: sec_core
rules:
- remote_ip_prefix: 0.0.0.0/0
protocol: icmp
- remote_ip_prefix: 0.0.0.0/0
protocol: tcp
port_range_min: 22
port_range_max: 22
- remote_ip_prefix: 0.0.0.0/0
protocol: tcp
port_range_min: 80
port_range_max: 80
- remote_ip_prefix: 0.0.0.0/0
protocol: tcp
port_range_min: 443
port_range_max: 443
- remote_ip_prefix: 0.0.0.0/0
protocol: tcp
port_range_min: 3389
port_range_max: 3389

mgr:
type: OS::Nova::Server
properties:
name: mgr
image: 'Windows 11 22H2 Enterprise [Evaluation]'
flavor: gx1.2c4r
key_name: { get_param: key_name }
networks:
- port: { get_resource: mgr_port }
user_data_format: RAW
user_data: |
#ps1_sysnative
#
# Windows 10 doesn't set hostname correctly
#
$name = (New-Object System.Net.WebClient).DownloadString("http://169.254.169.254/latest/meta-data/hostname")
$shortname = $name.split('.',2)[0]
if ( $env:computername -ne $shortname ) {
Rename-Computer $shortname
exit 1003 # 1003 - reboot and run the plugin again on next boot
# https://cloudbase-init.readthedocs.io/en/latest/tutorial.html#file-execution
}
mgr_port:
type: OS::Neutron::Port
properties:
network_id: { get_resource: private_net }
security_groups:
- default
- { get_resource: sec_core }
fixed_ips:
- subnet_id: { get_resource: private_subnet }
mgr_floating_ip:
type: OS::Neutron::FloatingIP
properties:
floating_network: ntnu-internal
port_id: { get_resource: mgr_port }

cl1:
type: OS::Nova::Server
properties:
name: cl1
image: 'Windows 11 22H2 Enterprise [Evaluation]'
flavor: gx1.2c4r
key_name: { get_param: key_name }
networks:
- port: { get_resource: cl1_port }
user_data_format: RAW
user_data: |
#ps1_sysnative
#
# Windows 10 doesn't set hostname correctly
#
$name = (New-Object System.Net.WebClient).DownloadString("http://169.254.169.254/latest/meta-data/hostname")
$shortname = $name.split('.',2)[0]
if ( $env:computername -ne $shortname ) {
Rename-Computer $shortname
exit 1003 # 1003 - reboot and run the plugin again on next boot
# https://cloudbase-init.readthedocs.io/en/latest/tutorial.html#file-execution
}
cl1_port:
type: OS::Neutron::Port
properties:
network_id: { get_resource: private_net }
security_groups:
- default
- { get_resource: sec_core }
fixed_ips:
- subnet_id: { get_resource: private_subnet }
cl1_floating_ip:
type: OS::Neutron::FloatingIP
properties:
floating_network: ntnu-internal
port_id: { get_resource: cl1_port }

dc1:
type: OS::Nova::Server
properties:
name: dc1
image: 'Windows Server 2025 Standard [Evaluation]'
flavor: gx1.2c4r
key_name: { get_param: key_name }
networks:
- port: { get_resource: dc1_port }
dc1_port:
type: OS::Neutron::Port
properties:
network_id: { get_resource: private_net }
security_groups:
- default
- { get_resource: sec_core }
fixed_ips:
- subnet_id: { get_resource: private_subnet }
dc1_floating_ip:
type: OS::Neutron::FloatingIP
properties:
floating_network: ntnu-internal
port_id: { get_resource: dc1_port }

srv1:
type: OS::Nova::Server
properties:
name: srv1
image: 'Windows Server 2025 Standard [Evaluation]'
flavor: gx1.2c4r
key_name: { get_param: key_name }
networks:
- port: { get_resource: srv1_port }
srv1_port:
type: OS::Neutron::Port
properties:
network_id: { get_resource: private_net }
security_groups:
- default
- { get_resource: sec_core }
fixed_ips:
- subnet_id: { get_resource: private_subnet }
srv1_floating_ip:
type: OS::Neutron::FloatingIP
properties:
floating_network: ntnu-internal
port_id: { get_resource: srv1_port }

outputs:
srv1_private_ip:
description: IP address of srv1 in private network
value: { get_attr: [ srv1, first_address ] }
srv1_public_ip:
description: Floating IP address of srv1 in public network
value: { get_attr: [ srv1_floating_ip, floating_ip_address ] }
dc1_private_ip:
description: IP address of dc1 in private network
value: { get_attr: [ dc1, first_address ] }
dc1_public_ip:
description: Floating IP address of dc1 in public network
value: { get_attr: [ dc1_floating_ip, floating_ip_address ] }
cl1_private_ip:
description: IP address of cl1 in private network
value: { get_attr: [ cl1, first_address ] }
cl1_public_ip:
description: Floating IP address of cl1 in public network
value: { get_attr: [ cl1_floating_ip, floating_ip_address ] }
mgr_private_ip:
description: IP address of mgr in private network
value: { get_attr: [ mgr, first_address ] }
mgr_public_ip:
description: Floating IP address of mgr in public network
value: { get_attr: [ mgr_floating_ip, floating_ip_address ] }
148 changes: 148 additions & 0 deletions scripts/CreateUserCSV.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# Usage:
# .\CreateUserCSV.ps1
# will create the csv-file which can be used like this (if sec.core domain prepared):
# $ADUsers = Import-Csv seccoreusers.csv -Delimiter ';'
# # Headers: Username;GivenName;SurName;UserPrincipalName;DisplayName;Password;Department;Path
# foreach ($User in $ADUsers) {
# if (!(Get-ADUser -LDAPFilter `
# "(sAMAccountName=$($User.Username))")) {
# New-ADUser `
# -SamAccountName $User.Username `
# -UserPrincipalName $User.UserPrincipalName `
# -Name $User.DisplayName `
# -GivenName $User.GivenName `
# -Surname $User.SurName `
# -Enabled $True `
# -ChangePasswordAtLogon $False `
# -DisplayName $user.Displayname `
# -Department $user.Department `
# -Path $user.path `
# -AccountPassword (ConvertTo-SecureString $user.Password -AsPlainText
# -Force)
# }
# }

# Run this script to create your own list of 100 users for the SEC.CORE
# infrastructure as a CSV-file
# Each time the script is run, it will create a new random combination
# of firstname (which is also the username), lastname and department
# New unique random passwords are generated for every user

# Test so we don't overwrite a file by accident
#
if ((Get-ChildItem -ErrorAction SilentlyContinue seccoreusers.csv).Exists)
{"You alread have the file seccoreusers.csv!"; return;}
if ($PSVersionTable.PSVersion.Major -eq 5)
{Write-Output "This script cannot be executed in Windows PowerShell, please use PowerShell core"; return;}

# 100 unique firstnames without norwegian characters ('øæå')
#
$FirstName = @("Nora","Emma","Ella","Maja","Olivia","Emilie","Sofie","Leah",
"Sofia","Ingrid","Frida","Sara","Tiril","Selma","Ada","Hedda",
"Amalie","Anna","Alma","Eva","Mia","Thea","Live","Ida","Astrid",
"Ellinor","Vilde","Linnea","Iben","Aurora","Mathilde","Jenny",
"Tuva","Julie","Oda","Sigrid","Amanda","Lilly","Hedvig",
"Victoria","Amelia","Josefine","Agnes","Solveig","Saga","Marie",
"Eline","Oline","Maria","Hege","Jakob","Emil","Noah","Oliver",
"Filip","William","Lucas","Liam","Henrik","Oskar","Aksel",
"Theodor","Elias","Kasper","Magnus","Johannes","Isak","Mathias",
"Tobias","Olav","Sander","Haakon","Jonas","Ludvig","Benjamin",
"Matheo","Alfred","Alexander","Victor","Markus","Theo",
"Mohammad","Herman","Adam","Ulrik","Iver","Sebastian","Johan",
"Odin","Leon","Nikolai","Even","Leo","Kristian","Mikkel",
"Gustav","Felix","Sverre","Adrian","Lars"
)

# 100 unique lastnames
#
$LastName = @("Hansen","Johansen","Olsen","Larsen","Andersen","Pedersen",
"Nilsen","Kristiansen","Jensen","Karlsen","Johnsen","Pettersen",
"Eriksen","Berg","Haugen","Hagen","Johannessen","Andreassen",
"Jacobsen","Dahl","Jørgensen","Henriksen","Lund","Halvorsen",
"Sørensen","Jakobsen","Moen","Gundersen","Iversen","Strand",
"Solberg","Svendsen","Eide","Knutsen","Martinsen","Paulsen",
"Bakken","Kristoffersen","Mathisen","Lie","Amundsen","Nguyen",
"Rasmussen","Ali","Lunde","Solheim","Berge","Moe","Nygård",
"Bakke","Kristensen","Fredriksen","Holm","Lien","Hauge",
"Christensen","Andresen","Nielsen","Knudsen","Evensen","Sæther",
"Aas","Myhre","Hanssen","Ahmed","Haugland","Thomassen",
"Sivertsen","Simonsen","Danielsen","Berntsen","Sandvik",
"Rønning","Arnesen","Antonsen","Næss","Vik","Haug","Ellingsen",
"Thorsen","Edvardsen","Birkeland","Isaksen","Gulbrandsen","Ruud",
"Aasen","Strøm","Myklebust","Tangen","Ødegård","Eliassen",
"Helland","Bøe","Jenssen","Aune","Mikkelsen","Tveit","Brekke",
"Abrahamsen","Madsen"
)

# 2 in IT, 8 in Adm and 30 consultants in each of in each of Blue, Red and DFIR
#
$OrgUnits = @("ou=IT,ou=AllUsers","ou=IT,ou=AllUsers",
"ou=Adm,ou=AllUsers","ou=Adm,ou=AllUsers","ou=Adm,ou=AllUsers",
"ou=Adm,ou=AllUsers","ou=Adm,ou=AllUsers","ou=Adm,ou=AllUsers",
"ou=Adm,ou=AllUsers","ou=Adm,ou=AllUsers",
"ou=Blue,ou=Cons,ou=AllUsers","ou=Blue,ou=Cons,ou=AllUsers",
"ou=Blue,ou=Cons,ou=AllUsers","ou=Blue,ou=Cons,ou=AllUsers",
"ou=Blue,ou=Cons,ou=AllUsers","ou=Blue,ou=Cons,ou=AllUsers",
"ou=Blue,ou=Cons,ou=AllUsers","ou=Blue,ou=Cons,ou=AllUsers",
"ou=Blue,ou=Cons,ou=AllUsers","ou=Blue,ou=Cons,ou=AllUsers",
"ou=Blue,ou=Cons,ou=AllUsers","ou=Blue,ou=Cons,ou=AllUsers",
"ou=Blue,ou=Cons,ou=AllUsers","ou=Blue,ou=Cons,ou=AllUsers",
"ou=Blue,ou=Cons,ou=AllUsers","ou=Blue,ou=Cons,ou=AllUsers",
"ou=Blue,ou=Cons,ou=AllUsers","ou=Blue,ou=Cons,ou=AllUsers",
"ou=Blue,ou=Cons,ou=AllUsers","ou=Blue,ou=Cons,ou=AllUsers",
"ou=Blue,ou=Cons,ou=AllUsers","ou=Blue,ou=Cons,ou=AllUsers",
"ou=Blue,ou=Cons,ou=AllUsers","ou=Blue,ou=Cons,ou=AllUsers",
"ou=Blue,ou=Cons,ou=AllUsers","ou=Blue,ou=Cons,ou=AllUsers",
"ou=Blue,ou=Cons,ou=AllUsers","ou=Blue,ou=Cons,ou=AllUsers",
"ou=Blue,ou=Cons,ou=AllUsers","ou=Blue,ou=Cons,ou=AllUsers",
"ou=Red,ou=Cons,ou=AllUsers","ou=Red,ou=Cons,ou=AllUsers",
"ou=Red,ou=Cons,ou=AllUsers","ou=Red,ou=Cons,ou=AllUsers",
"ou=Red,ou=Cons,ou=AllUsers","ou=Red,ou=Cons,ou=AllUsers",
"ou=Red,ou=Cons,ou=AllUsers","ou=Red,ou=Cons,ou=AllUsers",
"ou=Red,ou=Cons,ou=AllUsers","ou=Red,ou=Cons,ou=AllUsers",
"ou=Red,ou=Cons,ou=AllUsers","ou=Red,ou=Cons,ou=AllUsers",
"ou=Red,ou=Cons,ou=AllUsers","ou=Red,ou=Cons,ou=AllUsers",
"ou=Red,ou=Cons,ou=AllUsers","ou=Red,ou=Cons,ou=AllUsers",
"ou=Red,ou=Cons,ou=AllUsers","ou=Red,ou=Cons,ou=AllUsers",
"ou=Red,ou=Cons,ou=AllUsers","ou=Red,ou=Cons,ou=AllUsers",
"ou=Red,ou=Cons,ou=AllUsers","ou=Red,ou=Cons,ou=AllUsers",
"ou=Red,ou=Cons,ou=AllUsers","ou=Red,ou=Cons,ou=AllUsers",
"ou=Red,ou=Cons,ou=AllUsers","ou=Red,ou=Cons,ou=AllUsers",
"ou=Red,ou=Cons,ou=AllUsers","ou=Red,ou=Cons,ou=AllUsers",
"ou=Red,ou=Cons,ou=AllUsers","ou=Red,ou=Cons,ou=AllUsers",
"ou=DFIR,ou=Cons,ou=AllUsers","ou=DFIR,ou=Cons,ou=AllUsers",
"ou=DFIR,ou=Cons,ou=AllUsers","ou=DFIR,ou=Cons,ou=AllUsers",
"ou=DFIR,ou=Cons,ou=AllUsers","ou=DFIR,ou=Cons,ou=AllUsers",
"ou=DFIR,ou=Cons,ou=AllUsers","ou=DFIR,ou=Cons,ou=AllUsers",
"ou=DFIR,ou=Cons,ou=AllUsers","ou=DFIR,ou=Cons,ou=AllUsers",
"ou=DFIR,ou=Cons,ou=AllUsers","ou=DFIR,ou=Cons,ou=AllUsers",
"ou=DFIR,ou=Cons,ou=AllUsers","ou=DFIR,ou=Cons,ou=AllUsers",
"ou=DFIR,ou=Cons,ou=AllUsers","ou=DFIR,ou=Cons,ou=AllUsers",
"ou=DFIR,ou=Cons,ou=AllUsers","ou=DFIR,ou=Cons,ou=AllUsers",
"ou=DFIR,ou=Cons,ou=AllUsers","ou=DFIR,ou=Cons,ou=AllUsers",
"ou=DFIR,ou=Cons,ou=AllUsers","ou=DFIR,ou=Cons,ou=AllUsers",
"ou=DFIR,ou=Cons,ou=AllUsers","ou=DFIR,ou=Cons,ou=AllUsers",
"ou=DFIR,ou=Cons,ou=AllUsers","ou=DFIR,ou=Cons,ou=AllUsers",
"ou=DFIR,ou=Cons,ou=AllUsers","ou=DFIR,ou=Cons,ou=AllUsers",
"ou=DFIR,ou=Cons,ou=AllUsers","ou=DFIR,ou=Cons,ou=AllUsers"
)

# Three shuffled indices to randomly mix firstname, lastname, and department
#
$fnidx = 0..99 | Get-Random -Shuffle
$lnidx = 0..99 | Get-Random -Shuffle
$ouidx = 0..99 | Get-Random -Shuffle

Write-Output "UserName;GivenName;SurName;UserPrincipalName;DisplayName;Password;Department;Path" > seccoreusers.csv

foreach ($i in 0..99) {
$UserName = $FirstName[$fnidx[$i]].ToLower()
$GivenName = $FirstName[$fnidx[$i]]
$SurName = $LastName[$lnidx[$i]]
$UserPrincipalName = $UserName + '@' + 'sec.core'
$DisplayName = $GivenName + ' ' + $SurName
$Password = -join ('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPRSTUVWXYZ0123456789!#$%&()*+,-./:<=>?@[\]_{|}'.ToCharArray() | Get-Random -Count 16) + '1.aA'
$Department = ($OrgUnits[$ouidx[$i]] -split '[=,]')[1]
$Path = $OrgUnits[$ouidx[$i]] + ',' + "dc=SEC,dc=CORE"
Write-Output "$UserName;$GivenName;$SurName;$UserPrincipalName;$DisplayName;$Password;$Department;$Path" >> seccoreusers.csv
}
Loading

0 comments on commit 2f59d14

Please sign in to comment.