Detailed close-up of computer components including RAM and cables inside a server or high-performance PC.

[Share] Windows Configuration and Security Inspection Tool (with CSV Export Feature)

A simple Windows configuration and security inspection tool written in Go. Check OS versions, installed software, antivirus and firewall status, system services, and export results as CSV. Multi-language interface available (Traditional Chinese, English, Japanese, German). Ideal for ISO 27001 audits and personal system checks.

A configuration and security inspection tool written in Go

Author: Pecezen.org
Platform: Windows
Language: Go

When preparing for ISO/IEC 27001 certification, under control 8.9 Configuration Management, auditors commonly review the following aspects.

  • The operating system versions of servers and personal computers.
  • Whether any unauthorized software has been installed.
  • Whether firewall and antivirus software are properly enabled.
  • Whether any unnecessary system services are running.

So I decided to write a simple configuration and security inspection tool. I recently had some free time, so Iโ€™m sharing it here for everyoneโ€™s reference.

โœ… Tool Features Overview๏ผš

  • ๐Ÿ–ฅ๏ธ Hardware Information
  • ๐Ÿงฉ Operating System (OS) Information
  • ๐Ÿ“ฆ Installed Software List
  • ๐Ÿ›ก๏ธ Antivirus Status
  • ๐Ÿ”ฅ Firewall Status
  • โš™๏ธ System Services Status
  • ๐Ÿ“ค Supports CSV Export for auditing or record-keeping
  • ๐ŸŒ Multi-language Interface (Traditional Chinese / English / Japanese / German)

๐Ÿ–ฅ๏ธ User Interface๏ผš

๐Ÿ’พ Download the Windows Version

You can use the source code below to compile the program yourself, but weโ€™ve also included a ready-to-run Windows executable for your convenience.๏ผš

Download

The downloaded file is a compressed archive.
The extraction password is: pecezen.org

๐Ÿ”—Source Code

package main

import (
	"bytes"
	"encoding/csv"
	"image/color"
	"os/exec"
	"strings"
	"syscall"

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/app"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/dialog"
	"fyne.io/fyne/v2/theme"
	"fyne.io/fyne/v2/widget"
)

/*
=========================

	Theme & Language (i18n)

=========================
*/
type readableTheme struct{}

func (readableTheme) Color(n fyne.ThemeColorName, v fyne.ThemeVariant) color.Color {
	if n == theme.ColorNameDisabled {
		return color.NRGBA{0, 0, 0, 255}
	}
	return theme.DefaultTheme().Color(n, v)
}
func (readableTheme) Font(s fyne.TextStyle) fyne.Resource     { return theme.DefaultTheme().Font(s) }
func (readableTheme) Icon(n fyne.ThemeIconName) fyne.Resource { return theme.DefaultTheme().Icon(n) }
func (readableTheme) Size(n fyne.ThemeSizeName) float32       { return theme.DefaultTheme().Size(n) }

type Lang string

const (
	LangZHTW Lang = "zh-TW"
	LangEN   Lang = "en"
	LangJA   Lang = "ja"
	LangDE   Lang = "de"
)

var langOptions = []string{"็น้ซ”ไธญๆ–‡", "English", "ๆ—ฅๆœฌ่ชž", "Deutsch"}
var langMap = map[string]Lang{
	"็น้ซ”ไธญๆ–‡":    LangZHTW,
	"English": LangEN,
	"ๆ—ฅๆœฌ่ชž":     LangJA,
	"Deutsch": LangDE,
}

const prefLangKey = "SelectedLanguage"

type i18n struct{ lang Lang }

func (i i18n) tr(key string) string {
	table := map[string]map[Lang]string{
		"app.title": {
			LangZHTW: "็ต„ๆ…‹่ˆ‡ๅฎ‰ๅ…จๆ€งๆชขๆธฌๅทฅๅ…ท V1.0",
			LangEN:   "Config & Security Audit Tool V1.0",
			LangJA:   "ๆง‹ๆˆใจใ‚ปใ‚ญใƒฅใƒชใƒ†ใ‚ฃ่จบๆ–ญ V1.0",
			LangDE:   "Konfigurations- und Sicherheits-Audit-Tool V1.0",
		},
		"tab.hardware": {
			LangZHTW: "็กฌ้ซ”่ณ‡่จŠ", LangEN: "Hardware Info", LangJA: "ใƒใƒผใƒ‰ใ‚ฆใ‚งใ‚ขๆƒ…ๅ ฑ", LangDE: "Hardware-Info",
		},
		"tab.os": {
			LangZHTW: "OS ่ณ‡่จŠ", LangEN: "OS Info", LangJA: "OS ๆƒ…ๅ ฑ", LangDE: "OS-Info",
		},
		"tab.apps": {
			LangZHTW: "่ปŸ้ซ”ๆธ…ๅ–ฎ", LangEN: "Software", LangJA: "ใ‚ฝใƒ•ใƒˆไธ€่ฆง", LangDE: "Softwareliste",
		},
		"tab.av": {
			LangZHTW: "้˜ฒๆฏ’็‹€ๆ…‹", LangEN: "Antivirus", LangJA: "ใ‚ฆใ‚คใƒซใ‚นๅฏพ็ญ–", LangDE: "Virenschutz",
		},
		"tab.firewall": {
			LangZHTW: "้˜ฒ็ซ็‰†", LangEN: "Firewall", LangJA: "้˜ฒ็ซๅฃ", LangDE: "Firewall",
		},
		"tab.service": {
			LangZHTW: "็ณป็ตฑๆœๅ‹™", LangEN: "Services", LangJA: "ใ‚ตใƒผใƒ“ใ‚น", LangDE: "Systemdienste",
		},
		"tab.policy": {
			LangZHTW: "ๅฎ‰ๅ…จๅŽŸๅ‰‡", LangEN: "Policies", LangJA: "ใƒใƒชใ‚ทใƒผ", LangDE: "Sicherheitsrichtlinien",
		},
		"btn.export": {
			LangZHTW: "ๅŒฏๅ‡บ็›ฎๅ‰ๅˆ†้  CSV",
			LangEN:   "Export Current CSV",
			LangJA:   "็พๅœจใฎใƒšใƒผใ‚ธใ‚’CSVๅŒฏๅ‡บ",
			LangDE:   "Aktuellen Tab als CSV exportieren",
		},
		"btn.about": {
			LangZHTW: "้—œๆ–ผ", LangEN: "About", LangJA: "่ฉณใ—ใ", LangDE: "รœber",
		},
		"loading": {
			LangZHTW: "่ณ‡ๆ–™่ผ‰ๅ…ฅไธญ...", LangEN: "Loading...", LangJA: "่ชญใฟ่พผใฟไธญ...", LangDE: "Daten werden geladen...",
		},
		"col.name": {
			LangZHTW: "ๅ็จฑ (Name)", LangEN: "Name", LangJA: "ๅๅ‰", LangDE: "Name",
		},
		"col.status": {
			LangZHTW: "็‹€ๆ…‹/็‰ˆๆœฌ (Status/Ver)", LangEN: "Status/Ver", LangJA: "็Šถๆ…‹/็‰ˆ", LangDE: "Status/Ver",
		},
		"col.detail": {
			LangZHTW: "่ฉณ็ดฐ่ณ‡่จŠ (Details)", LangEN: "Details", LangJA: "่ฉณ็ดฐๆƒ…ๅ ฑ", LangDE: "Details",
		},
		"about.title": {
			LangZHTW: "้—œๆ–ผ็จ‹ๅผ", LangEN: "About App", LangJA: "ใ‚ขใƒ—ใƒชใซใคใ„ใฆ", LangDE: "รœber die App",
		},
		"about.body": {
			LangZHTW: "ไฝœ่€…: Pecezen.org\n็‰ˆๆฌŠๆ‰€ๆœ‰ ยฉ 2026\n\nๅ…่ฒฌ่ฒๆ˜Ž:\nไฝœ่€…ไธๅฐๅ› ไฝฟ็”จๆœฌ็จ‹ๅผๆ‰€้€ ๆˆ็š„ไปปไฝ•่ณ‡ๆ–™้บๅคฑใ€ๆๅฃžๆˆ–ๅ…ถไป–ๅพŒๆžœ่ฒ ่ฒฌใ€‚",
			LangEN:   "Author: Pecezen.org\nCopyright ยฉ 2026\n\nDisclaimer:\nThe author is not responsible for any data loss, damage, or other consequences caused by the use of this program.",
			LangJA:   "ไฝœ่€…: Pecezen.org\n่‘—ไฝœๆฌŠ ยฉ 2026\n\nๅ…่ฒฌไบ‹้ …:\nไฝœ่€…ใฏใ€ๆœฌใƒ—ใƒญใ‚ฐใƒฉใƒ ใฎไฝฟ็”จใซใ‚ˆใฃใฆ็”Ÿใ˜ใŸใƒ‡ใƒผใ‚ฟใฎ็ด›ๅคฑใ€็ ดๆใ€ใพใŸใฏใใฎไป–ใฎ็ตๆžœใซใคใ„ใฆไธ€ๅˆ‡ใฎ่ฒฌไปปใ‚’่ฒ ใ„ใพใ›ใ‚“ใ€‚",
			LangDE:   "Autor: Pecezen.org\nUrheberrecht ยฉ 2026\n\nHaftungsausschluss:\nDer Autor haftet nicht fรผr Datenverlust, Schรคden oder andere Folgen, die durch die Verwendung dieses Programms entstehen.",
		},
	}
	return table[key][i.lang]
}

/*
=========================

	Custom UI Components

=========================
*/
type fixedWidthLabel struct {
	widget.Label
	width float32
}

func newFixedWidthLabel(text string, width float32) *fixedWidthLabel {
	l := &fixedWidthLabel{width: width}
	l.ExtendBaseWidget(l)
	l.SetText(text)
	l.TextStyle = fyne.TextStyle{Bold: true}
	return l
}
func (f *fixedWidthLabel) MinSize() fyne.Size { return fyne.NewSize(f.width, 32) }

/*
=========================

	PowerShell Data Logic

=========================
*/
func runCmd(psCmd string) string {
	cmd := exec.Command("powershell", "-NoProfile", "-Command", "[Console]::OutputEncoding=[System.Text.Encoding]::UTF8;"+psCmd)
	cmd.SysProcAttr = &syscall.SysProcAttr{
		HideWindow:    true,
		CreationFlags: 0x08000000,
	}
	var out bytes.Buffer
	cmd.Stdout = &out
	_ = cmd.Run()
	return strings.TrimSpace(out.String())
}

func collectHardwareInfo() string {
	ps := `
    $cpu = (Get-CimInstance Win32_Processor).Name;
    $cs = Get-CimInstance Win32_ComputerSystem;
    $mem = $cs.TotalPhysicalMemory;
    Write-Host "CPU: $cpu";
    Write-Host "Total Memory: $([math]::Round($mem/1GB, 2)) GB";
    Write-Host "Manufacturer: $($cs.Manufacturer)";
    Write-Host "Model: $($cs.Model)";
    Write-Host "---------------------------------------------------------------------------";
    Write-Host "Disk Partition Info (็กฌ็ขŸๅˆ†ๅ€่ณ‡่จŠ):";
    Get-CimInstance Win32_LogicalDisk | Where-Object {$_.DriveType -eq 3} | ForEach-Object {
        $size = [math]::Round($_.Size/1GB, 2); $free = [math]::Round($_.FreeSpace/1GB, 2); $used = $size - $free; $per = [math]::Round(($used/$size)*100, 2);
        Write-Host "Drive $($_.DeviceID) | Total: $($size)GB | Free: $($free)GB | Used: $($per)%"
    } | Out-String`
	return runCmd(ps)
}

func collectAntivirusInfo() string {
	ps := `
    Write-Host "[ ๅฎ‰่ฃ็š„้˜ฒๆฏ’่ปŸ้ซ”ๆธ…ๅ–ฎ ]";
    $avList = Get-CimInstance -Namespace root/SecurityCenter2 -ClassName AntivirusProduct;
    if ($avList) { $avList | ForEach-Object { Write-Host "- ่ปŸ้ซ”ๅ็จฑ: $($_.displayName)" } } else { Write-Host "- ๆœชๅตๆธฌๅˆฐ็ฌฌไธ‰ๆ–น้˜ฒๆฏ’่ปŸ้ซ”" }
    Write-Host ""; Write-Host "[ Windows Defender ็‹€ๆ…‹ ]";
    Get-MpComputerStatus | Select-Object AntivirusEnabled, RealTimeProtectionEnabled, AntivirusSignatureVersion, AntivirusSignatureLastUpdated | Format-List | Out-String`
	return runCmd(ps)
}

func collectListData(psCmd string) [][]string {
	raw := runCmd(psCmd + " | ConvertTo-Csv -NoTypeInformation")
	r := csv.NewReader(strings.NewReader(raw))
	records, err := r.ReadAll()
	if err != nil || len(records) <= 1 {
		return [][]string{}
	}
	return records[1:]
}

/*
=========================

	Main Application

=========================
*/
func main() {
	a := app.NewWithID("org.pecezen.audit.tool")
	a.Settings().SetTheme(&readableTheme{})

	savedLang := a.Preferences().StringWithFallback(prefLangKey, "็น้ซ”ไธญๆ–‡")
	i := i18n{lang: langMap[savedLang]}

	w := a.NewWindow(i.tr("app.title"))

	if resourceIconPng != nil {
		w.SetIcon(resourceIconPng)
	}
	w.Resize(fyne.NewSize(1100, 560))

	appData := [][]string{}
	svcData := [][]string{}
	var currentTabIndex int = 0

	const nameColWidth float32 = 780
	const statusColWidth float32 = 250

	createTextTab := func() (*widget.Entry, fyne.CanvasObject, *fixedWidthLabel) {
		e := widget.NewMultiLineEntry()
		e.Disable()
		e.TextStyle = fyne.TextStyle{Monospace: true}
		headerLabel := newFixedWidthLabel(i.tr("col.detail"), nameColWidth+statusColWidth)
		return e, container.NewBorder(container.NewHBox(headerLabel), nil, nil, nil, container.NewScroll(e)), headerLabel
	}

	createTableTab := func(data *[][]string) (fyne.CanvasObject, *widget.Table, *fixedWidthLabel, *fixedWidthLabel) {
		h1 := newFixedWidthLabel(i.tr("col.name"), nameColWidth)
		h2 := newFixedWidthLabel(i.tr("col.status"), statusColWidth)
		t := widget.NewTable(
			func() (int, int) { return len(*data), 2 },
			func() fyne.CanvasObject { l := widget.NewLabel(""); l.Truncation = fyne.TextTruncateEllipsis; return l },
			func(id widget.TableCellID, o fyne.CanvasObject) {
				if id.Row < len(*data) {
					o.(*widget.Label).SetText((*data)[id.Row][id.Col])
				}
			},
		)
		t.SetColumnWidth(0, nameColWidth)
		t.SetColumnWidth(1, statusColWidth)
		return container.NewBorder(container.NewHBox(h1, h2), nil, nil, nil, t), t, h1, h2
	}

	entryHW, uiHW, lblHW := createTextTab()
	entryOS, uiOS, lblOS := createTextTab()
	entryAV, uiAV, lblAV := createTextTab()
	entryFW, uiFW, lblFW := createTextTab()
	entryPol, uiPol, lblPol := createTextTab()
	uiApps, tableApps, appH1, appH2 := createTableTab(&appData)
	uiSvc, tableSvc, svcH1, svcH2 := createTableTab(&svcData)

	tabs := container.NewAppTabs(
		container.NewTabItem(i.tr("tab.hardware"), uiHW),
		container.NewTabItem(i.tr("tab.os"), uiOS),
		container.NewTabItem(i.tr("tab.apps"), uiApps),
		container.NewTabItem(i.tr("tab.av"), uiAV),
		container.NewTabItem(i.tr("tab.firewall"), uiFW),
		container.NewTabItem(i.tr("tab.service"), uiSvc),
		container.NewTabItem(i.tr("tab.policy"), uiPol),
	)

	tabs.OnSelected = func(ti *container.TabItem) {
		for idx, item := range tabs.Items {
			if item == ti {
				currentTabIndex = idx
				break
			}
		}
	}

	refresh := func() {
		p := dialog.NewProgressInfinite(i.tr("app.title"), i.tr("loading"), w)
		p.Show()
		go func() {
			sHW := collectHardwareInfo()
			sOS := runCmd("Get-ComputerInfo | Select-Object OsName, OsVersion, OsArchitecture, WindowsVersion, OsInstallDate | Format-List | Out-String")
			sAV := collectAntivirusInfo()
			sFW := runCmd("Get-NetFirewallProfile | Select-Object Name, Enabled | Out-String")
			sPol := runCmd("net accounts")
			appData = collectListData(`Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | Where-Object {$_.DisplayName} | Select-Object DisplayName, DisplayVersion`)
			svcData = collectListData("Get-Service | Where-Object {$_.Status -eq 'Running'} | Select-Object DisplayName, Status")

			fyne.Do(func() {
				entryHW.SetText(sHW)
				entryOS.SetText(sOS)
				entryAV.SetText(sAV)
				entryFW.SetText(sFW)
				entryPol.SetText(sPol)
				lblHW.SetText(i.tr("col.detail"))
				lblOS.SetText(i.tr("col.detail"))
				lblAV.SetText(i.tr("col.detail"))
				lblFW.SetText(i.tr("col.detail"))
				lblPol.SetText(i.tr("col.detail"))
				appH1.SetText(i.tr("col.name"))
				appH2.SetText(i.tr("col.status"))
				svcH1.SetText(i.tr("col.name"))
				svcH2.SetText(i.tr("col.status"))
				tableApps.Refresh()
				tableSvc.Refresh()
				p.Hide()
			})
		}()
	}

	exportBtn := widget.NewButton(i.tr("btn.export"), func() {
		save := dialog.NewFileSave(func(uc fyne.URIWriteCloser, err error) {
			if uc == nil {
				return
			}
			defer uc.Close()
			var buf bytes.Buffer
			buf.Write([]byte{0xEF, 0xBB, 0xBF})
			writer := csv.NewWriter(&buf)
			switch currentTabIndex {
			case 0:
				writer.Write([]string{"Hardware Info"})
				writer.Write([]string{entryHW.Text})
			case 1:
				writer.Write([]string{"OS Info"})
				writer.Write([]string{entryOS.Text})
			case 2:
				writer.Write([]string{"Software Name", "Version"})
				writer.WriteAll(appData)
			case 3:
				writer.Write([]string{"Antivirus Status"})
				writer.Write([]string{entryAV.Text})
			case 4:
				writer.Write([]string{"Firewall Status"})
				writer.Write([]string{entryFW.Text})
			case 5:
				writer.Write([]string{"Service Name", "Status"})
				writer.WriteAll(svcData)
			case 6:
				writer.Write([]string{"Security Policy"})
				writer.Write([]string{entryPol.Text})
			}
			writer.Flush()
			uc.Write(buf.Bytes())
		}, w)
		tabNames := []string{"Hardware", "OS", "Software", "Antivirus", "Firewall", "Services", "Policies"}
		save.SetFileName("Audit_" + tabNames[currentTabIndex] + ".csv")
		save.Show()
	})

	aboutBtn := widget.NewButton(i.tr("btn.about"), func() {
		dialog.ShowInformation(i.tr("about.title"), i.tr("about.body"), w)
	})

	langSelect := widget.NewSelect(langOptions, func(s string) {
		a.Preferences().SetString(prefLangKey, s)
		i.lang = langMap[s]
		w.SetTitle(i.tr("app.title"))
		exportBtn.SetText(i.tr("btn.export"))
		aboutBtn.SetText(i.tr("btn.about"))
		for idx, key := range []string{"tab.hardware", "tab.os", "tab.apps", "tab.av", "tab.firewall", "tab.service", "tab.policy"} {
			tabs.Items[idx].Text = " " + i.tr(key) + " "
		}
		tabs.Refresh()
		refresh()
	})

	langSelect.SetSelected(savedLang)

	controls := container.NewHBox(widget.NewLabel("Lang:"), langSelect, exportBtn, aboutBtn)
	w.SetContent(container.NewBorder(container.NewVBox(controls), nil, nil, nil, tabs))
	w.ShowAndRun()
}

โš ๏ธ Disclaimer

The author is not responsible for any data loss, damage, or other consequences resulting from the use of this program.


Leave a Reply

Your email address will not be published. Required fields are marked *