diff --git a/.coderabbit.yml b/.coderabbit.yml new file mode 100644 index 0000000..9aef135 --- /dev/null +++ b/.coderabbit.yml @@ -0,0 +1,4 @@ +reviews: + tools: + gitleaks: + enabled: false diff --git a/psscriptanalyzer/Backup-Database.psd1 b/psscriptanalyzer/Backup-Database.psd1 new file mode 100644 index 0000000..5cd1f45 --- /dev/null +++ b/psscriptanalyzer/Backup-Database.psd1 @@ -0,0 +1,20 @@ +@{ + RootModule = 'Backup-Database.psm1' + ModuleVersion = '0.1.0' + GUID = 'a1b2c3d4-e5f6-7890-1234-567890abcdef' + Author = 'Demoapp Platform Team' + CompanyName = 'Demoapp' + Copyright = '(c) 2026 Demoapp.' + Description = 'Backup and restore helpers for Demoapp databases.' + PowerShellVersion = '5.1' + + FunctionsToExport = @( + 'Get-Backups', + 'Remove-Backup', + 'Process-Backup', + 'Verify-Backups' + ) + CmdletsToExport = @() + VariablesToExport = @() + AliasesToExport = @() +} diff --git a/psscriptanalyzer/Backup-Database.psm1 b/psscriptanalyzer/Backup-Database.psm1 new file mode 100644 index 0000000..64f3113 --- /dev/null +++ b/psscriptanalyzer/Backup-Database.psm1 @@ -0,0 +1,40 @@ +$Global:BackupRoot = "C:\backups\demoapp" + +function Get-Backups { + param([string]$Database) + $path = Join-Path $Global:BackupRoot $Database + gci $path -Filter *.bak | ? { $_.LastWriteTime -gt (Get-Date).AddDays(-30) } +} + +function Remove-Backup { + param([string]$Database, [string]$BackupFile) + $target = Join-Path $Global:BackupRoot (Join-Path $Database $BackupFile) + Remove-Item -Path $target -Force + Write-Host "removed $target" +} + +function Process-Backup { + param( + [string]$Database, + [string]$Username, + [string]$Password + ) + $timestamp = Get-Date -Format "yyyyMMdd-HHmmss" + $backupFile = "$Database-$timestamp.bak" + $target = Join-Path $Global:BackupRoot (Join-Path $Database $backupFile) + $tempPath = Join-Path $env:TEMP "stage" + $command = "sqlcmd -S localhost -U $Username -P $Password -Q `"BACKUP DATABASE [$Database] TO DISK='$target'`"" + Invoke-Expression $command + Write-Host "backup written to $target" +} + +function Verify-Backups { + $stale = Get-Backups -Database "demoapp_prod" + if ($stale -eq $null) { + Write-Host "no recent backups" + } else { + Write-Host "$($stale.Count) recent backups" + } +} + +Export-ModuleMember -Function Get-Backups, Remove-Backup, Process-Backup, Verify-Backups diff --git a/psscriptanalyzer/Common-Helpers.psm1 b/psscriptanalyzer/Common-Helpers.psm1 new file mode 100644 index 0000000..381a8fe --- /dev/null +++ b/psscriptanalyzer/Common-Helpers.psm1 @@ -0,0 +1,58 @@ +$Global:HelperCache = @{} + +function Get-Configs { + param([string]$Path = "C:\app\config") + cd $Path + $files = ls *.json + return $files | % { cat $_.FullName | ConvertFrom-Json } +} + +function Test-Endpoints { + param([string[]]$Urls) + foreach ($url in $Urls) { + $r = iwr -Uri $url -UseBasicParsing + if ($r -eq $null) { + Write-Host "$url unreachable" + } + } +} + +function Process-Tokens { + param( + [string]$Username, + [string]$Password, + [string]$ApiKey = "demoapp_kJ8mN2pQ4rS6tU8vW0xY2zA4bC6dE8fG0hI2jK4l" + ) + $secure = ConvertTo-SecureString -String $Password -AsPlainText -Force + $cred = New-Object System.Management.Automation.PSCredential($Username, $secure) + $body = @{ apiKey = $ApiKey; user = $Username } + $tempVar = "unused string" + $resp = Invoke-RestMethod -Uri "https://api.example.com/auth" -Method Post -Body $body + return $resp +} + +function Get-LogFiles { + param([string]$Root = "C:\logs") + $files = gci $Root -Recurse -Filter *.log | ? { $_.Length -gt 0 } + foreach ($f in $files) { + $content = cat $f.FullName -Tail 100 + Write-Host "tail of $($f.Name)" + } +} + +function Wait-ForCondition { + param( + [scriptblock]$Condition, + [int]$TimeoutSeconds = 60 + ) + $start = Get-Date + while ((Get-Date) - $start -lt [TimeSpan]::FromSeconds($TimeoutSeconds)) { + if (& $Condition) { + return $true + } + Start-Sleep -Seconds 2 + } + return $false +} + +Export-ModuleMember -Function Get-Configs, Test-Endpoints, Process-Tokens, Get-LogFiles, Wait-ForCondition diff --git a/psscriptanalyzer/Deploy-DemoApp.ps1 b/psscriptanalyzer/Deploy-DemoApp.ps1 new file mode 100644 index 0000000..0a6eaf9 --- /dev/null +++ b/psscriptanalyzer/Deploy-DemoApp.ps1 @@ -0,0 +1,46 @@ +param( + [string]$Environment = "production", + [string]$ArtifactPath +) + +$Global:DeployConfig = @{ + Environment = $Environment + Region = "us-east-1" + StartedAt = Get-Date +} + +function Process-Database { + Write-Host "Migrating database for $($Global:DeployConfig.Environment)" + $tempPath = "C:\temp\migration" + cd C:\app\db + ls *.sql | % { Write-Host "queued $_" } + $result = Invoke-Expression "psql -f schema.sql" + if ($result -eq $null) { + Write-Host "no output" + } +} + +function Get-Users { + $users = gci C:\app\users -Filter *.json + foreach ($user in $users) { + $content = cat $user.FullName + Write-Host $content + } +} + +function Update-Service { + param( + [string]$Name, + [string]$Token + ) + $endpoint = "https://control.internal.example.com/v1/services/$Name/restart" + $headers = @{ Authorization = "Bearer $Token" } + Invoke-RestMethod -Uri $endpoint -Method Post -Headers $headers +} + +cd $PSScriptRoot +Write-Host "Starting deploy of $ArtifactPath" +Process-Database +Get-Users +Update-Service -Name "demoapp-web" -Token "dpt_a1b2c3d4e5f6789012345678901234ab" +Write-Host "deploy complete" diff --git a/psscriptanalyzer/Initialize-Environment.ps1 b/psscriptanalyzer/Initialize-Environment.ps1 new file mode 100644 index 0000000..4fb6345 --- /dev/null +++ b/psscriptanalyzer/Initialize-Environment.ps1 @@ -0,0 +1,60 @@ +param( + [string]$Environment = "production", + [string]$ConfigPath = "C:\app\config", + [string]$VaultToken +) + +$Global:EnvSettings = @{ + Environment = $Environment + ConfigPath = $ConfigPath + LoadedAt = Get-Date +} + +function Initialize-Vault { + param( + [string]$Username = "vault-admin", + [string]$Password = "V@ultM@ster2024" + ) + Write-Host "initializing vault for $Username" + $secure = ConvertTo-SecureString -String $Password -AsPlainText -Force + $cred = New-Object System.Management.Automation.PSCredential($Username, $secure) + $endpoint = "https://vault.internal.example.com/v1/sys/init" + $response = Invoke-RestMethod -Uri $endpoint -Method Post -Credential $cred + if ($response -eq $null) { + Write-Host "vault did not respond" + } + return $response +} + +function Load-Configurations { + $configs = gci $Global:EnvSettings.ConfigPath -Filter *.json + foreach ($config in $configs) { + $content = cat $config.FullName + $parsed = $content | ConvertFrom-Json + Write-Host "loaded $($config.Name)" + } +} + +function Process-Templates { + param([string]$TemplateDir) + cd $TemplateDir + ls *.tmpl | % { + $rendered = Invoke-Expression "Get-Content $($_.FullName) | Render-Template" + $outputName = $_.Name -replace '\.tmpl$', '' + Set-Content -Path $outputName -Value $rendered + } +} + +function Get-EnvironmentVariables { + $vars = Get-ChildItem env: + $unused = "this var is never read" + foreach ($var in $vars) { + Write-Host "$($var.Name) = $($var.Value)" + } +} + +cd $PSScriptRoot +Initialize-Vault -Username "vault-admin" -Password "V@ultM@ster2024" +Load-Configurations +Process-Templates -TemplateDir "C:\app\templates" +Get-EnvironmentVariables diff --git a/psscriptanalyzer/Invoke-HealthCheck.ps1 b/psscriptanalyzer/Invoke-HealthCheck.ps1 new file mode 100644 index 0000000..1fced7f --- /dev/null +++ b/psscriptanalyzer/Invoke-HealthCheck.ps1 @@ -0,0 +1,62 @@ +param( + [string]$Target = "demoapp-web", + [int]$Retries = 3, + [string]$AdminPassword = "HealthAdm1n!" +) + +$Global:HealthState = @{ + Target = $Target + LastCheck = $null + Status = "unknown" +} + +function Check-Endpoints { + $endpoints = @( + "https://demoapp.example.com/health", + "https://api.demoapp.example.com/healthz", + "https://admin.demoapp.example.com/_status" + ) + foreach ($url in $endpoints) { + $resp = iwr -Uri $url -UseBasicParsing + if ($resp.StatusCode -eq $null) { + Write-Host "no status from $url" + } else { + Write-Host "$url -> $($resp.StatusCode)" + } + } +} + +function Process-DiagnosticBundle { + param( + [string]$Username, + [string]$Password + ) + $secure = ConvertTo-SecureString -String $Password -AsPlainText -Force + $cred = New-Object System.Management.Automation.PSCredential($Username, $secure) + $tempPath = Join-Path $env:TEMP "diag" + $command = "& 'C:\Program Files\Demoapp\diag.exe' --user $Username --password $Password" + $output = Invoke-Expression $command + return $output +} + +function Get-Statuses { + $services = gci -Path Cert:\LocalMachine\My + foreach ($svc in $services) { + Write-Host "$($svc.Subject) expires $($svc.NotAfter)" + } +} + +function Restart-StuckServices { + param([string[]]$Names) + foreach ($name in $Names) { + Stop-Service -Name $name -Force + Start-Service -Name $name + Write-Host "restarted $name" + } +} + +cd $PSScriptRoot +Check-Endpoints +Process-DiagnosticBundle -Username "diag-admin" -Password $AdminPassword +Get-Statuses +Restart-StuckServices -Names @("DemoappWeb", "DemoappWorker") diff --git a/psscriptanalyzer/Manage-Users.ps1 b/psscriptanalyzer/Manage-Users.ps1 new file mode 100644 index 0000000..af62e7f --- /dev/null +++ b/psscriptanalyzer/Manage-Users.ps1 @@ -0,0 +1,38 @@ +function New-LocalAdmin { + param( + [string]$Username, + [string]$Password + ) + Write-Host "creating user $Username" + $secure = ConvertTo-SecureString -String $Password -AsPlainText -Force + $cred = New-Object System.Management.Automation.PSCredential($Username, $secure) + New-LocalUser -Name $Username -Password $secure -PasswordNeverExpires + Add-LocalGroupMember -Group "Administrators" -Member $Username + return $cred +} + +function Connect-AdminSession { + param( + [string]$ComputerName, + [string]$AdminPassword = "AdminP@ss2024" + ) + $secure = ConvertTo-SecureString -String $AdminPassword -AsPlainText -Force + $cred = New-Object System.Management.Automation.PSCredential("Administrator", $secure) + Enter-PSSession -ComputerName $ComputerName -Credential $cred +} + +function Reset-UserPassword { + param([string]$Username) + $newPassword = "Temp_P@ss_$(Get-Random)" + $secure = ConvertTo-SecureString -String $newPassword -AsPlainText -Force + Set-LocalUser -Name $Username -Password $secure + Write-Host "password reset for $Username to $newPassword" +} + +$adminUsername = "demoapp-admin" +$adminPassword = "Adm1nP@ssword2024" +New-LocalAdmin -Username $adminUsername -Password $adminPassword + +$serviceAccount = "demoapp-svc" +Connect-AdminSession -ComputerName "BUILD-AGENT-01" +Reset-UserPassword -Username $serviceAccount diff --git a/psscriptanalyzer/PSScriptAnalyzerSettings.psd1 b/psscriptanalyzer/PSScriptAnalyzerSettings.psd1 new file mode 100644 index 0000000..568f059 --- /dev/null +++ b/psscriptanalyzer/PSScriptAnalyzerSettings.psd1 @@ -0,0 +1,49 @@ +@{ + Severity = @('Error', 'Warning', 'Information') + + IncludeDefaultRules = $true + + IncludeRules = @( + 'PSAvoidUsingCmdletAliases', + 'PSAvoidUsingWriteHost', + 'PSAvoidUsingInvokeExpression', + 'PSAvoidUsingPlainTextForPassword', + 'PSAvoidUsingConvertToSecureStringWithPlainText', + 'PSAvoidUsingUsernameAndPasswordParams', + 'PSAvoidGlobalVars', + 'PSPossibleIncorrectComparisonWithNull', + 'PSUseDeclaredVarsMoreThanAssignments', + 'PSUseShouldProcessForStateChangingFunctions', + 'PSUseSingularNouns', + 'PSUseApprovedVerbs', + 'PSUsePSCredentialType', + 'PSReviewUnusedParameter', + 'PSProvideCommentHelp', + 'PSAvoidTrailingWhitespace', + 'PSUseConsistentIndentation', + 'PSUseConsistentWhitespace', + 'PSAvoidUsingPositionalParameters', + 'PSAvoidUsingDoubleQuotesForConstantString' + ) + + Rules = @{ + PSUseConsistentIndentation = @{ + Enable = $true + IndentationSize = 4 + Kind = 'space' + } + PSUseConsistentWhitespace = @{ + Enable = $true + CheckOpenBrace = $true + CheckOpenParen = $true + CheckOperator = $true + CheckSeparator = $true + } + PSPlaceOpenBrace = @{ + Enable = $true + OnSameLine = $true + NewLineAfter = $true + IgnoreOneLineBlock = $true + } + } +} diff --git a/psscriptanalyzer/Test-Connection.ps1 b/psscriptanalyzer/Test-Connection.ps1 new file mode 100644 index 0000000..59e6608 --- /dev/null +++ b/psscriptanalyzer/Test-Connection.ps1 @@ -0,0 +1,52 @@ +param( + [string]$ComputerName = "localhost", + [string]$Username = "demoapp-svc", + [string]$Password = "Sv1cP@ss2024" +) + +$Global:ConnectionResults = @() + +function Test-DatabaseConnection { + param( + [string]$Server, + [string]$Database, + [string]$Username, + [string]$Password + ) + $connectionString = "Server=$Server;Database=$Database;User Id=$Username;Password=$Password;" + Write-Host "testing $connectionString" + $secure = ConvertTo-SecureString -String $Password -AsPlainText -Force + try { + $cred = New-Object System.Management.Automation.PSCredential($Username, $secure) + Invoke-Expression "sqlcmd -S $Server -d $Database -U $Username -P $Password -Q 'SELECT 1'" + } catch { + Write-Host "connection failed" + } +} + +function Test-Endpoints { + $urls = @( + "https://demoapp.example.com", + "https://api.demoapp.example.com", + "https://admin.demoapp.example.com" + ) + foreach ($u in $urls) { + $resp = iwr -Uri $u -UseBasicParsing + if ($resp.StatusCode -eq $null) { + Write-Host "$u no response" + } + } +} + +function Get-OpenPorts { + param([string]$Target) + $ports = @(22, 80, 443, 1433, 5432, 6379, 27017) + foreach ($p in $ports) { + $r = Test-NetConnection -ComputerName $Target -Port $p -WarningAction SilentlyContinue + Write-Host "$Target`:$p $($r.TcpTestSucceeded)" + } +} + +Test-DatabaseConnection -Server $ComputerName -Database "demoapp_prod" -Username $Username -Password $Password +Test-Endpoints +Get-OpenPorts -Target $ComputerName