#Requires -Version 5.1 <# .SYNOPSIS Initial database setup for ROA Oracle. .DESCRIPTION Performs initial database configuration including: - Validates Oracle installation - Auto-detects CDB vs non-CDB configuration - Configures DEFAULT_PROFILE (disables password expiry) - Configures sqlnet.ora for Instant Client 10/11 compatibility - Writes sqlnet.ora and tnsnames.ora into the machine TNS_ADMIN as well - Pins LOCAL_LISTENER to a literal address (no tnsnames alias dependency) - Creates tablespace ROA - Creates user CONTAFIN_ORACLE with required privileges - Rewrites the CONTAFIN_ORACLE and SYSTEM passwords so both get a 10G verifier .PARAMETER OracleHome Oracle home directory. If not specified, auto-detects from registry or environment. .PARAMETER ServiceName Database service name. Auto-detects: ROA for non-CDB, XEPDB1 for Oracle XE CDB. .PARAMETER SystemPassword SYSTEM user password. Default: romfastsoft .PARAMETER ContafinPassword CONTAFIN_ORACLE user password. Default: ROMFASTSOFT .PARAMETER DatafileDir Directory for tablespace datafiles. Auto-detects if not specified. .PARAMETER TablespaceSize Initial size of ROA tablespace in MB. Default: 1000 .PARAMETER SkipSqlnetConfig Skip sqlnet.ora configuration. .PARAMETER ResetSystemPassword Reseteaza parola lui SYSTEM in CDB root, cu aceeasi valoare ($SystemPassword). Necesar DOAR pe o baza mai veche, unde SYSTEM a apucat sa intre in EXPIRED(GRACE) inainte ca profilul din CDB root sa fie corectat (Step 4b): statusul nu se curata singur cand profilul devine UNLIMITED, cere un ALTER USER. Pe o instalare noua nu e nevoie - contul nu apuca sa expire. Implicit OFF, pentru ca schimba o parola. .EXAMPLE .\01-setup-database.ps1 .EXAMPLE .\01-setup-database.ps1 -ServiceName "XEPDB1" -SystemPassword "mypassword" .EXAMPLE .\01-setup-database.ps1 -OracleHome "C:\app\oracle\product\21c\dbhomeXE" -DatafileDir "D:\oradata" .NOTES File Name : 01-setup-database.ps1 Prerequisite : Oracle Database installed, PowerShell 5.1+ Copyright 2024 : ROMFAST #> [CmdletBinding()] param( [Parameter(Mandatory = $false)] [string]$OracleHome, [Parameter(Mandatory = $false)] [string]$ServiceName, [Parameter(Mandatory = $false)] [string]$DbHost, [Parameter(Mandatory = $false)] [int]$Port = 1521, [Parameter(Mandatory = $false)] [string]$SystemPassword = "romfastsoft", [Parameter(Mandatory = $false)] [string]$ContafinPassword = "ROMFASTSOFT", [Parameter(Mandatory = $false)] [string]$DatafileDir, [Parameter(Mandatory = $false)] [int]$TablespaceSize = 1000, [Parameter(Mandatory = $false)] [switch]$SkipSqlnetConfig, [Parameter(Mandatory = $false)] [switch]$ResetSystemPassword, # Calea fizica a directorului Oracle DMPDIR. Aceeasi valoare trebuie data # tuturor pasilor care ating DMPDIR (01, 02, 03, 05, 06), altfel ultimul # care ruleaza repozitioneaza obiectul si ceilalti citesc din alta parte. [Parameter(Mandatory = $false)] [string]$DmpDir = "C:\DMPDIR" ) $ErrorActionPreference = 'Stop' # Load config.ps1 if exists (provides defaults) $configPath = Join-Path (Split-Path $PSScriptRoot -Parent) "config.ps1" if (Test-Path $configPath) { . $configPath # Use config values if parameters not provided if (-not $OracleHome -and $ORACLE_HOME) { $OracleHome = $ORACLE_HOME } if (-not $ServiceName -and $SERVICE_NAME) { $ServiceName = $SERVICE_NAME } if (-not $DbHost -and $DB_HOST) { $DbHost = $DB_HOST } if (-not $Port -and $DB_PORT) { $Port = $DB_PORT } if ($SystemPassword -eq "romfastsoft" -and $SYSTEM_PASSWORD) { $SystemPassword = $SYSTEM_PASSWORD } if ($ContafinPassword -eq "ROMFASTSOFT" -and $CONTAFIN_PASSWORD) { $ContafinPassword = $CONTAFIN_PASSWORD } if (-not $DatafileDir -and $DATAFILE_DIR) { $DatafileDir = $DATAFILE_DIR } } # Source library functions . "$PSScriptRoot\lib\logging-functions.ps1" . "$PSScriptRoot\lib\oracle-functions.ps1" # Initialize logging $logPath = Join-Path $PSScriptRoot "..\logs\01-setup-database_$(Get-Date -Format 'yyyyMMdd_HHmmss').log" Initialize-LogFile -LogPath $logPath -ScriptName "01-setup-database.ps1" try { Write-LogSection "ROA Database Initial Setup" # Step 1: Validate Oracle installation Write-Log "Detecting Oracle installation..." $oraHome = Get-OracleHome -OracleHome $OracleHome Write-LogSuccess "Oracle Home: $oraHome" # Step 2: Auto-detect service name if not specified if (-not $ServiceName) { Write-Log "Auto-detecting service name..." # First try to connect to XE to check CDB status try { $version = Get-OracleVersion -OracleHome $oraHome -ServiceName "XE" -DbHost $DbHost -Port $Port -Password $SystemPassword if ($version.IsCDB) { $ServiceName = "XEPDB1" Write-Log "Detected CDB installation, using PDB: XEPDB1" } else { $ServiceName = "XE" Write-Log "Detected non-CDB installation, using service: XE" } } catch { # Try ROA as fallback try { $null = Test-OracleConnection -OracleHome $oraHome -ServiceName "ROA" -DbHost $DbHost -Port $Port -Password $SystemPassword $ServiceName = "ROA" Write-Log "Using existing service: ROA" } catch { $ServiceName = "XEPDB1" Write-Log "Defaulting to XEPDB1" } } } Write-LogSuccess "Service Name: $ServiceName" if ($DbHost) { Write-Log "Database Host: ${DbHost}:${Port}" } # Step 3: Test connection Write-Log "Testing database connection..." if (-not (Test-OracleConnection -OracleHome $oraHome -ServiceName $ServiceName -DbHost $DbHost -Port $Port -Password $SystemPassword)) { throw "Cannot connect to database. Please verify ServiceName, Host and SystemPassword." } Write-LogSuccess "Database connection successful" # Get version info $versionInfo = Get-OracleVersion -OracleHome $oraHome -ServiceName $ServiceName -DbHost $DbHost -Port $Port -Password $SystemPassword Write-Log "Oracle Version: $($versionInfo.Version)" Write-Log "Oracle Edition: $($versionInfo.Edition)" Write-Log "CDB Mode: $($versionInfo.IsCDB)" # Check container info $containerInfo = Test-PDB -OracleHome $oraHome -ServiceName $ServiceName -DbHost $DbHost -Port $Port -Password $SystemPassword Write-Log "Container: $($containerInfo.ContainerName)" if ($containerInfo.IsPDB) { Write-Log "Connected to PDB (Pluggable Database)" } elseif ($containerInfo.IsNonCDB) { Write-Log "Connected to non-CDB database" } # Step 4: Configure DEFAULT_PROFILE (no password expiry) Write-LogSection "Configuring DEFAULT_PROFILE" $profileSql = @" SET ECHO OFF FEEDBACK ON VERIFY OFF PROMPT Configuring DEFAULT_PROFILE - disabling password expiration... ALTER PROFILE DEFAULT LIMIT PASSWORD_LIFE_TIME UNLIMITED PASSWORD_GRACE_TIME UNLIMITED PASSWORD_REUSE_TIME UNLIMITED PASSWORD_REUSE_MAX UNLIMITED FAILED_LOGIN_ATTEMPTS UNLIMITED PASSWORD_LOCK_TIME UNLIMITED; COMMIT; SELECT 'PROFILE_CONFIGURED' FROM dual; EXIT; "@ $result = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName -DbHost $DbHost -Port $Port ` -Username "SYS" -Password $SystemPassword -SqlCommand $profileSql -AsSysdba if ($result -match "PROFILE_CONFIGURED") { Write-LogSuccess "DEFAULT_PROFILE configured successfully" } else { Write-LogWarning "Could not verify profile configuration" Write-LogDebug $result } # Step 4b: Acelasi profil si in CDB root # ALTER PROFILE dintr-un PDB NU acopera CDB root: acolo DEFAULT ramane cu # PASSWORD_LIFE_TIME 180, deci SYSTEM din root expira la ~6 luni de la instalare # (ORA-28002, apoi ORA-28001). Verificat pe VM 302: SYSTEM era EXPIRED(GRACE). # Se comuta pe CDB$ROOT ca SYSDBA si se altereaza profilul local de acolo. if ($containerInfo.IsPDB) { Write-Log "Applying DEFAULT_PROFILE in CDB root as well..." $profileCdbSql = @" SET ECHO OFF FEEDBACK ON VERIFY OFF ALTER SESSION SET CONTAINER = CDB`$ROOT; ALTER PROFILE DEFAULT LIMIT PASSWORD_LIFE_TIME UNLIMITED PASSWORD_GRACE_TIME UNLIMITED PASSWORD_REUSE_TIME UNLIMITED PASSWORD_REUSE_MAX UNLIMITED FAILED_LOGIN_ATTEMPTS UNLIMITED PASSWORD_LOCK_TIME UNLIMITED; SELECT 'PROFILE_CDB_CONFIGURED' FROM dual; EXIT; "@ $resultCdb = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName -DbHost $DbHost -Port $Port ` -Username "SYS" -Password $SystemPassword -SqlCommand $profileCdbSql -AsSysdba if ($resultCdb -match "PROFILE_CDB_CONFIGURED" -and $resultCdb -notmatch "ORA-") { Write-LogSuccess "DEFAULT_PROFILE configured in CDB root" } else { Write-LogWarning "Could not configure DEFAULT_PROFILE in CDB root" Write-LogWarning "SYSTEM din CDB root va expira la ~180 de zile. Manual:" Write-LogWarning " ALTER SESSION SET CONTAINER=CDB`$ROOT;" Write-LogWarning " ALTER PROFILE DEFAULT LIMIT PASSWORD_LIFE_TIME UNLIMITED CONTAINER=ALL;" Write-LogDebug $resultCdb } # Step 4c (optional): curata EXPIRED(GRACE) pe SYSTEM din CDB root. # ALTER PROFILE opreste expirarile viitoare, dar NU reseteaza statusul # unui cont deja intrat in perioada de gratie - asta cere un ALTER USER. # Opt-in, fiindca scrie o parola. if ($ResetSystemPassword) { Write-Log "Resetting SYSTEM password in CDB root (clears EXPIRED(GRACE))..." $resetSql = @" SET ECHO OFF FEEDBACK ON VERIFY OFF ALTER SESSION SET CONTAINER = CDB`$ROOT; ALTER USER SYSTEM IDENTIFIED BY "$SystemPassword"; SELECT 'SYSTEM_STATUS:' || account_status AS rez FROM dba_users WHERE username = 'SYSTEM'; EXIT; "@ $resetResult = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName -DbHost $DbHost -Port $Port ` -Username "SYS" -Password $SystemPassword -SqlCommand $resetSql -AsSysdba if ($resetResult -match "SYSTEM_STATUS:OPEN" -and $resetResult -notmatch "ORA-") { Write-LogSuccess "SYSTEM in CDB root: OPEN" } else { Write-LogWarning "Nu am putut confirma resetarea lui SYSTEM in CDB root. Manual:" Write-LogWarning " ALTER SESSION SET CONTAINER=CDB`$ROOT;" Write-LogWarning " ALTER USER SYSTEM IDENTIFIED BY ;" Write-LogDebug $resetResult } } } # Step 5: Configure sqlnet.ora for old client compatibility if (-not $SkipSqlnetConfig) { Write-LogSection "Configuring sqlnet.ora" # Oracle 21c uses read-only Oracle Home - config goes to 'homes' directory # Check for Oracle 21c homes directory structure first $oraBase = Split-Path (Split-Path (Split-Path $oraHome -Parent) -Parent) -Parent $homesDir = Join-Path $oraBase "product\21c\homes" # Find the actual Oracle Base Home (where user configs go) $networkAdmin = $null if (Test-Path $homesDir) { # Oracle 21c with read-only Oracle Home $oraBaseHome = Get-ChildItem -Path $homesDir -Directory | Where-Object { $_.Name -match "OraDB" } | Select-Object -First 1 if ($oraBaseHome) { $networkAdmin = Join-Path $oraBaseHome.FullName "network\admin" Write-Log "Detected Oracle 21c read-only Home structure" Write-Log "Using Oracle Base Home: $($oraBaseHome.FullName)" } } # Fallback to traditional location if (-not $networkAdmin -or -not (Test-Path (Split-Path $networkAdmin -Parent))) { $networkAdmin = Join-Path $oraHome "network\admin" Write-Log "Using traditional Oracle Home network/admin location" } $sqlnetOra = Join-Path $networkAdmin "sqlnet.ora" if (-not (Test-Path -Path $networkAdmin)) { New-Item -ItemType Directory -Path $networkAdmin -Force | Out-Null } # Backup existing sqlnet.ora if (Test-Path -Path $sqlnetOra) { $backupPath = "$sqlnetOra.bak_$(Get-Date -Format 'yyyyMMdd_HHmmss')" Copy-Item -Path $sqlnetOra -Destination $backupPath -Force Write-Log "Backed up existing sqlnet.ora to $backupPath" } # Create sqlnet.ora with compatibility settings $sqlnetContent = @" # sqlnet.ora - ROA Oracle Configuration # Generated by 01-setup-database.ps1 on $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') # Allow connections from old Oracle Instant Client 10g/11g (ODBC drivers) # These clients don't support newer authentication methods SQLNET.ALLOWED_LOGON_VERSION_SERVER=8 SQLNET.ALLOWED_LOGON_VERSION_CLIENT=8 # Disable encryption requirement for old clients SQLNET.ENCRYPTION_SERVER = ACCEPTED SQLNET.CRYPTO_CHECKSUM_SERVER = ACCEPTED # Timp maxim pentru finalizarea handshake-ului de conectare (setare de server). # Nu limiteaza durata operatiilor de dupa conectare. SQLNET.INBOUND_CONNECT_TIMEOUT = 60 # ATENTIE: NU pune aici SQLNET.RECV_TIMEOUT / SQLNET.SEND_TIMEOUT. # Fisierul asta e citit si de clientii de pe aceeasi masina (sqlplus, impdp, # expdp). RECV_TIMEOUT limiteaza cat asteapta CLIENTUL un pachet de raspuns, # iar in timpul unui import Data Pump serverul poate lucra minute intregi fara # sa trimita nimic (creare de indecsi, constrangeri). Cu RECV_TIMEOUT = 30, # clientul impdp moare cu ORA-12609 "TNS: Receive timeout occurred", desi jobul # de pe server continua si se termina cu bine. Rezultatul e si mai perfid decat # un esec curat: 03-import-contafin.ps1 numara obiectele imediat dupa moartea # clientului, gaseste 0 si raporteaza esec pentru un import care a reusit. # Verificat pe VM 302 (2026-08-25). # TNS Names directory NAMES.DIRECTORY_PATH = (TNSNAMES, EZCONNECT) "@ Set-Content -Path $sqlnetOra -Value $sqlnetContent -Encoding ASCII Write-LogSuccess "sqlnet.ora configured at $sqlnetOra" Write-Log "Old client compatibility enabled (SQLNET.ALLOWED_LOGON_VERSION=8)" # Gazda pentru aliasurile TNS: IP-ul din LAN, nu localhost, ca acelasi # fisier sa functioneze si copiat pe statiile de lucru. $gazdaTns = Get-ServerLanIPv4 if (-not $gazdaTns) { $gazdaTns = $env:COMPUTERNAME Write-LogWarning "Nu am putut determina IP-ul din LAN; folosesc numele masinii ($gazdaTns) in tnsnames.ora." } # Doua aliasuri, nu unul, pentru ca doua consumatoare diferite cer # nume diferite pentru ACEEASI baza: # # ROA - aplicatia (NOM_FIRME.NUME_SERVER) si PACK_UPDATE. # XEPDB1 - numele serviciului. 11-setup-backup-export.ps1 scrie in # schema.txt linii de forma SCHEMA;PAROLA@$ServiceName, iar # backupora.exe le da mai departe lui expdp. # # Daca al doilea lipseste, expdp cade instant cu ORA-12154, nu scrie # niciun DMP, iar backupora.exe logheaza TOTUSI "DONE" pentru fiecare # schema: exportul zilnic nu exista, si nimic nu tipa. # Constatat la VADECO pe 2026-08-28 - patru zile de directoare goale. # # Nu atingem aliasurile generate de Oracle (XE, LISTENER_XE, # ORACLR_CONNECTION_DATA): daca $ServiceName e chiar unul dintre ele, # exista deja si e corect. $aliasuriOracle = @('XE', 'LISTENER_XE', 'ORACLR_CONNECTION_DATA') $aliasuriNoastre = @('ROA') if ($ServiceName -and $ServiceName -ne 'ROA' -and $aliasuriOracle -notcontains $ServiceName) { $aliasuriNoastre += $ServiceName } $roaAliasBlock = ($aliasuriNoastre | ForEach-Object { @" # Alias $_ - necesar si serverului, nu doar statiilor. Vezi comentariul din # 01-setup-database.ps1 si docs/lectii-actualizare-roa-alias-tns.md. $_ = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = $gazdaTns)(PORT = $Port)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = $ServiceName) ) ) "@ }) -join "`r`n" # Aliasul ROA trebuie sa existe SI in tnsnames.ora al Oracle Home-ului, # nu doar in cel din TNS_ADMIN (instantclient). # # PACK_UPDATE (actualizarea ROA) genereaza D:\DMPDIR\script_master.sql si # lanseaza un sqlplus EXTERN care incepe cu # CONNECT CONTAFIN_ORACLE/...@ # adica, in practica, @ROA. Scriptul are WHENEVER SQLERROR EXIT, deci # iese la prima linie daca aliasul nu se rezolva - iar jobul # UPDATEROA_ZILNIC raporteaza TOTUSI SUCCEEDED, pentru ca el doar # lanseaza procesul si se termina in ~5 secunde. # # sqlplus-ul lansat de baza mosteneste mediul serviciului Oracle. Daca # instanta a pornit INAINTE ca TNS_ADMIN de masina sa existe - cazul # normal la o instalare noua, unde variabila o scriem noi mai jos - # serviciul nu are TNS_ADMIN si cade pe tnsnames.ora de aici. Fara # aliasul de mai jos: ORA-12154, si actualizarea ROA nu se aplica # niciodata, complet tacut. # Constatat la VADECO pe 2026-08-28, dupa migrare: actualizarea nu # rulase niciodata pe serverul acela. Detalii in # docs/lectii-actualizare-roa-alias-tns.md. $homeTnsNames = Join-Path $networkAdmin "tnsnames.ora" if (Test-Path $homeTnsNames) { $stampHomeTns = Get-Date -Format 'yyyyMMdd_HHmmss' Copy-Item $homeTnsNames "$homeTnsNames.bak_$stampHomeTns" -Force # Scot un eventual alias ROA existent (rulare anterioara, alt IP) si # il rescriu, ca operatia sa fie idempotenta. Restul fisierului - # XE, LISTENER_XE, ORACLR_CONNECTION_DATA - ramane neatins: sunt # generate de Oracle si folosite de extproc si de inregistrarea la # listener. # Sterg si comentariile lipite imediat deasupra lui "ROA =" (fara # rand gol intre): sunt antetul blocului nostru si, altfel, s-ar # acumula cate o copie la fiecare rulare. Un rand gol opreste # potrivirea, deci antetul fisierului ramane intact. # Curat FIECARE alias pe care il scriem noi, nu doar ROA: altfel # cel de-al doilea (numele serviciului) s-ar dubla la fiecare # rulare, pentru ca regexul lui ROA se opreste exact la el. $continutTns = Get-Content $homeTnsNames -Raw foreach ($alias in $aliasuriNoastre) { $continutTns = [regex]::Replace($continutTns, '(?ms)(^[ \t]*#[^\r\n]*\r?\n)*^[ \t]*' + [regex]::Escape($alias) + '[ \t]*=.*?(?=^[ \t]*[A-Za-z0-9_\.]+[ \t]*=|\z)', '') } $continutTns = $continutTns.TrimEnd() + "`r`n`r`n" + $roaAliasBlock } else { $continutTns = "# tnsnames.ora - generat de 01-setup-database.ps1`r`n`r`n" + $roaAliasBlock } Set-Content -Path $homeTnsNames -Value $continutTns -Encoding ASCII Write-LogSuccess "tnsnames.ora (Oracle Home): aliasuri $($aliasuriNoastre -join ', ') -> ${gazdaTns}:$Port/$ServiceName" # Daca exista TNS_ADMIN la nivel de masina, ACELA castiga - si pentru # clienti, si pentru procesele bazei, care mostenesc variabilele de # masina la pornirea serviciului. Instalarea ROAClient il pune sa arate # spre instantclient-ul ei (D:\ROA\instantclient_11_2_0_2), unde de # obicei nu exista niciun sqlnet.ora: atunci serverul cade pe valorile # implicite, adica ALLOWED_LOGON_VERSION_SERVER=12, si clientii vechi # primesc ORA-28040 desi fisierul de mai sus a fost scris corect. # Scriem acelasi continut si acolo, altfel toata configurarea de # compatibilitate e degeaba. # Atentie: efectul apare abia la primul restart al serviciului Oracle - # instanta pornita INAINTE ca TNS_ADMIN sa existe nu il vede. De aceea # instalarea poate parea perfecta si sa cada la primul reboot. Detalii # in docs/lectii-conectare-client-oracle.md. $tnsAdmin = [Environment]::GetEnvironmentVariable('TNS_ADMIN', 'Machine') if ($tnsAdmin) { $tnsAdminFull = [IO.Path]::GetFullPath($tnsAdmin.TrimEnd('\')) if ($tnsAdminFull -ne [IO.Path]::GetFullPath($networkAdmin.TrimEnd('\'))) { Write-LogWarning "TNS_ADMIN de masina arata spre $tnsAdminFull - acolo se citeste sqlnet.ora, nu din Oracle Home." if (-not (Test-Path $tnsAdminFull)) { New-Item -ItemType Directory -Path $tnsAdminFull -Force | Out-Null } $stampTns = Get-Date -Format 'yyyyMMdd_HHmmss' $tnsSqlnet = Join-Path $tnsAdminFull "sqlnet.ora" if (Test-Path $tnsSqlnet) { Copy-Item $tnsSqlnet "$tnsSqlnet.bak_$stampTns" -Force } Set-Content -Path $tnsSqlnet -Value $sqlnetContent -Encoding ASCII Write-LogSuccess "sqlnet.ora scris si in TNS_ADMIN: $tnsSqlnet" # tnsnames.ora din TNS_ADMIN - doua motive, amandoua serioase: # 1. ROAClient cauta aici aliasul ROA. Sablonul livrat cu el # arata spre HOST=SERVER_ROA / SID=ROA, care nu exista. # 2. instanta rezolva LOCAL_LISTENER tot de aici dupa un # restart. La Oracle XE, LOCAL_LISTENER e implicit aliasul # LISTENER_XE, definit doar in tnsnames.ora din Oracle # Home. Cand instanta reporneste cu TNS_ADMIN pe folderul # instantclient-ului, aliasul nu se mai rezolva, instanta # nu se inregistreaza la listener si TOTI clientii iau # ORA-12514 - cu baza sus si listenerul pornit. # $gazdaTns e calculat mai sus, o singura data, ca aliasul ROA # sa fie identic in ambele fisiere. $tnsNames = Join-Path $tnsAdminFull "tnsnames.ora" if (Test-Path $tnsNames) { $aliasuriVechi = @([regex]::Matches((Get-Content $tnsNames -Raw), '(?m)^\s*([A-Za-z0-9_\.]+)\s*=') | ForEach-Object { $_.Groups[1].Value }) Copy-Item $tnsNames "$tnsNames.bak_$stampTns" -Force Write-LogWarning "Rescriu $tnsNames (aliasuri existente: $($aliasuriVechi -join ', ')). Copia veche: tnsnames.ora.bak_$stampTns" } # Acelasi $roaAliasBlock ca in Oracle Home: o singura sursa # pentru aliasuri, ca cele doua fisiere sa nu poata diverge. $tnsContent = @" # tnsnames.ora - generat de 01-setup-database.ps1 pe $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') # # HOST e IP-ul serverului din LAN, nu localhost: acelasi fisier trebuie sa # functioneze si copiat pe statiile de lucru. $roaAliasBlock # Folosit de instanta pentru LOCAL_LISTENER. Fara el, o instanta care # porneste cu TNS_ADMIN aici nu se mai inregistreaza la listener. LISTENER_XE = (ADDRESS = (PROTOCOL = TCP)(HOST = $gazdaTns)(PORT = $Port)) "@ Set-Content -Path $tnsNames -Value $tnsContent -Encoding ASCII Write-LogSuccess "tnsnames.ora scris in TNS_ADMIN: aliasuri $($aliasuriNoastre -join ', ') -> ${gazdaTns}:$Port/$ServiceName" } } # Restart listener to apply changes Write-Log "Restarting listener to apply sqlnet.ora changes..." $lsnrctl = Join-Path $oraHome "bin\lsnrctl.exe" if (Test-Path $lsnrctl) { try { & $lsnrctl stop 2>&1 | Out-Null Start-Sleep -Seconds 3 $lsnrOutput = & $lsnrctl start 2>&1 Write-LogSuccess "Listener restarted successfully" # Wait for services to register (can take 30-60 seconds) Write-Log "Waiting for database services to register with listener..." $maxWait = 60 $waited = 0 $interval = 5 while ($waited -lt $maxWait) { Start-Sleep -Seconds $interval $waited += $interval # Check if our service is registered $lsnrStatus = & $lsnrctl status 2>&1 | Out-String if ($lsnrStatus -match $ServiceName) { Write-Log "Service $ServiceName registered after ${waited}s" break } Write-Log "Waiting... (${waited}s/${maxWait}s)" } if ($waited -ge $maxWait) { Write-LogWarning "Service may not be fully registered yet. Continuing anyway..." } } catch { Write-LogWarning "Could not restart listener: $_" Write-Log "Please restart listener manually: lsnrctl stop && lsnrctl start" } } # -------------------------------------------------------------------- # LOCAL_LISTENER ca adresa literala, nu ca alias # -------------------------------------------------------------------- # La Oracle XE, LOCAL_LISTENER e implicit aliasul LISTENER_XE, care se # rezolva din tnsnames.ora - deci depinde de TNS_ADMIN. Daca cineva # instaleaza ROAClient dupa noi (sau muta folderul instantclient), # aliasul nu se mai rezolva la urmatoarea pornire a instantei, aceasta # nu se mai inregistreaza la listener si toti clientii iau ORA-12514. # Adresa literala e in spfile si nu depinde de niciun fisier de retea. Write-LogSection "LOCAL_LISTENER" $gazdaListener = Get-ServerLanIPv4 if (-not $gazdaListener) { $gazdaListener = $env:COMPUTERNAME Write-LogWarning "Nu am putut determina IP-ul din LAN; pun $gazdaListener in LOCAL_LISTENER." } $numeDbSql = @" SET PAGESIZE 0 FEEDBACK OFF VERIFY OFF HEADING OFF ECHO OFF SELECT 'DBNAME:' || name FROM v`$database; EXIT; "@ $numeDbOut = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName -DbHost $DbHost -Port $Port ` -Username "SYSTEM" -Password $SystemPassword -SqlCommand $numeDbSql -Silent # LOCAL_LISTENER e parametru de instanta: intr-un CDB se pune din root, # al carui serviciu poarta numele bazei (XE la Oracle XE). $serviciiDeIncercat = @() if ($numeDbOut -match 'DBNAME:(\S+)') { $serviciiDeIncercat += $Matches[1].Trim() } if ($serviciiDeIncercat -notcontains $ServiceName) { $serviciiDeIncercat += $ServiceName } $llSql = @" SET FEEDBACK OFF VERIFY OFF HEADING OFF PAGESIZE 0 ALTER SYSTEM SET LOCAL_LISTENER='(ADDRESS=(PROTOCOL=TCP)(HOST=$gazdaListener)(PORT=$Port))' SCOPE=BOTH; ALTER SYSTEM REGISTER; SELECT 'LL_DONE' FROM DUAL; EXIT; "@ $llPus = $false foreach ($srv in $serviciiDeIncercat) { $llOut = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $srv -DbHost $DbHost -Port $Port ` -Username "SYSTEM" -Password $SystemPassword -SqlCommand $llSql -Silent if ($llOut -match 'LL_DONE') { Write-LogSuccess "LOCAL_LISTENER = (ADDRESS=(PROTOCOL=TCP)(HOST=$gazdaListener)(PORT=$Port)), pus prin serviciul $srv" $llPus = $true break } Write-LogDebug "LOCAL_LISTENER prin serviciul ${srv}: $llOut" } if (-not $llPus) { Write-LogWarning "Nu am putut seta LOCAL_LISTENER. Ramane pe alias (LISTENER_XE)." Write-LogWarning "Risc: dupa un restart al instantei cu alt TNS_ADMIN, inregistrarea la listener poate esua (ORA-12514)." } } else { Write-Log "Skipping sqlnet.ora configuration as requested" } # Step 6: Get datafile path if (-not $DatafileDir) { Write-Log "Auto-detecting datafile path..." $DatafileDir = Get-DatafilePath -OracleHome $oraHome -ServiceName $ServiceName -Password $SystemPassword } Write-LogSuccess "Datafile directory: $DatafileDir" # Step 7: Create tablespace ROA Write-LogSection "Creating Tablespace ROA" # Check if tablespace already exists $checkTsSql = @" SET PAGESIZE 0 FEEDBACK OFF VERIFY OFF HEADING OFF ECHO OFF SELECT 'TS_EXISTS' FROM dba_tablespaces WHERE tablespace_name = 'ROA'; EXIT; "@ $tsResult = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName -DbHost $DbHost -Port $Port ` -Username "SYSTEM" -Password $SystemPassword -SqlCommand $checkTsSql -Silent if ($tsResult -match "TS_EXISTS") { Write-LogWarning "Tablespace ROA already exists, skipping creation" } else { $datafilePath = Join-Path $DatafileDir "TS_ROA.DBF" $createTsSql = @" SET ECHO OFF FEEDBACK ON VERIFY OFF PROMPT Creating tablespace ROA... CREATE SMALLFILE TABLESPACE "ROA" DATAFILE '$datafilePath' SIZE ${TablespaceSize}M AUTOEXTEND ON NEXT 10M MAXSIZE UNLIMITED LOGGING EXTENT MANAGEMENT LOCAL SEGMENT SPACE MANAGEMENT AUTO; COMMIT; SELECT 'TS_CREATED' FROM dual; EXIT; "@ $result = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName -DbHost $DbHost -Port $Port ` -Username "SYSTEM" -Password $SystemPassword -SqlCommand $createTsSql if ($result -match "TS_CREATED") { Write-LogSuccess "Tablespace ROA created successfully" Write-Log "Datafile: $datafilePath" } else { Write-LogError "Failed to create tablespace ROA" Write-LogDebug $result throw "Tablespace creation failed" } } # Step 8: Create user CONTAFIN_ORACLE Write-LogSection "Creating User CONTAFIN_ORACLE" # Check if user already exists if (Test-OracleUser -OracleHome $oraHome -ServiceName $ServiceName ` -Password $SystemPassword -SchemaName "CONTAFIN_ORACLE") { Write-LogWarning "User CONTAFIN_ORACLE already exists" # Update password $updatePwdSql = @" SET ECHO OFF FEEDBACK ON VERIFY OFF ALTER USER CONTAFIN_ORACLE IDENTIFIED BY "$ContafinPassword"; COMMIT; SELECT 'PWD_UPDATED' FROM dual; EXIT; "@ $result = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName -DbHost $DbHost -Port $Port ` -Username "SYSTEM" -Password $SystemPassword -SqlCommand $updatePwdSql -Silent if ($result -match "PWD_UPDATED") { Write-Log "Password updated for CONTAFIN_ORACLE" } } else { $createUserSql = @" SET ECHO OFF FEEDBACK ON VERIFY OFF PROMPT Creating user CONTAFIN_ORACLE... CREATE USER CONTAFIN_ORACLE IDENTIFIED BY "$ContafinPassword" DEFAULT TABLESPACE ROA TEMPORARY TABLESPACE TEMP PROFILE DEFAULT; PROMPT Granting role privileges... GRANT CONNECT TO CONTAFIN_ORACLE; GRANT RESOURCE TO CONTAFIN_ORACLE; PROMPT Granting system privileges... GRANT CREATE ANY CONTEXT TO CONTAFIN_ORACLE; GRANT CREATE SESSION TO CONTAFIN_ORACLE; GRANT CREATE VIEW TO CONTAFIN_ORACLE; GRANT DELETE ANY TABLE TO CONTAFIN_ORACLE; GRANT DROP ANY CONTEXT TO CONTAFIN_ORACLE; GRANT EXECUTE ANY PROCEDURE TO CONTAFIN_ORACLE; GRANT INSERT ANY TABLE TO CONTAFIN_ORACLE; GRANT SELECT ANY DICTIONARY TO CONTAFIN_ORACLE; GRANT SELECT ANY SEQUENCE TO CONTAFIN_ORACLE; GRANT SELECT ANY TABLE TO CONTAFIN_ORACLE; GRANT UNLIMITED TABLESPACE TO CONTAFIN_ORACLE; GRANT UPDATE ANY TABLE TO CONTAFIN_ORACLE; GRANT CREATE PUBLIC SYNONYM TO CONTAFIN_ORACLE; GRANT DROP PUBLIC SYNONYM TO CONTAFIN_ORACLE; COMMIT; SELECT 'USER_CREATED' FROM dual; EXIT; "@ $result = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName -DbHost $DbHost -Port $Port ` -Username "SYSTEM" -Password $SystemPassword -SqlCommand $createUserSql if ($result -match "USER_CREATED") { Write-LogSuccess "User CONTAFIN_ORACLE created successfully" } else { Write-LogError "Failed to create user CONTAFIN_ORACLE" Write-LogDebug $result throw "User creation failed" } } # Verify user privileges $verifyUserSql = @" SET PAGESIZE 100 FEEDBACK OFF VERIFY OFF HEADING ON ECHO OFF LINESIZE 200 SELECT privilege FROM dba_sys_privs WHERE grantee = 'CONTAFIN_ORACLE' ORDER BY privilege; EXIT; "@ $privileges = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName -DbHost $DbHost -Port $Port ` -Username "SYSTEM" -Password $SystemPassword -SqlCommand $verifyUserSql -Silent Write-Log "CONTAFIN_ORACLE privileges verified" # Step 9: Create DMPDIR directory for Data Pump Write-LogSection "Creating Data Pump Directory" if (-not (New-OracleDirectory -OracleHome $oraHome -ServiceName $ServiceName ` -Password $SystemPassword -DirectoryName "DMPDIR" -DirectoryPath $DmpDir)) { throw "Directorul Data Pump DMPDIR ($DmpDir) nu e utilizabil de catre baza. Vezi mesajele de mai sus." } Write-LogSuccess "Data Pump directory DMPDIR created: $DmpDir" # Step 10: Reset passwords with 10G verifier (required for old client auth) # After sqlnet.ora is configured, passwords must be reset to generate 10G hash if (-not $SkipSqlnetConfig) { Write-LogSection "Resetting Passwords for Old Client Compatibility" Write-Log "Resetting passwords to generate 10G password verifiers..." $resetPwdSql = @" SET ECHO OFF FEEDBACK ON VERIFY OFF -- Reset CONTAFIN_ORACLE password to generate 10G verifier ALTER USER CONTAFIN_ORACLE IDENTIFIED BY "$ContafinPassword"; -- Verify password versions SELECT USERNAME, PASSWORD_VERSIONS FROM DBA_USERS WHERE USERNAME = 'CONTAFIN_ORACLE'; SELECT 'PWD_RESET_DONE' FROM DUAL; EXIT; "@ $result = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $ServiceName -DbHost $DbHost -Port $Port ` -Username "SYSTEM" -Password $SystemPassword -SqlCommand $resetPwdSql if ($result -match "PWD_RESET_DONE") { Write-LogSuccess "Password reset completed - 10G verifier now available" if ($result -match "10G") { Write-Log "Verified: Password has 10G verifier for old client compatibility" } } else { Write-LogWarning "Could not verify password reset" } # SYSTEM are nevoie de acelasi tratament, si nu e o rafinare. # Cu SQLNET.ALLOWED_LOGON_VERSION_SERVER=8, serverul autentifica orice # client pre-12c pe baza verificatorului 10G. Un cont care nu are 10G # in PASSWORD_VERSIONS (SYSTEM, dupa o instalare 21c, are doar 11G 12C) # nu se mai poate conecta din Instant Client 10/11 - si eroarea NU e # ORA-28040, ci ORA-01017 "invalid username/password", care trimite pe # o pista gresita ore intregi. Aceeasi parola, doar rescrisa, produce # verificatorul care lipseste. $sysPwdSql = if ($versionInfo.IsCDB) { @" SET ECHO OFF FEEDBACK OFF VERIFY OFF HEADING OFF PAGESIZE 0 ALTER USER SYSTEM IDENTIFIED BY "$SystemPassword" CONTAINER=ALL; SELECT 'SYS_PWD_DONE' FROM DUAL; EXIT; "@ } else { @" SET ECHO OFF FEEDBACK OFF VERIFY OFF HEADING OFF PAGESIZE 0 ALTER USER SYSTEM IDENTIFIED BY "$SystemPassword"; SELECT 'SYS_PWD_DONE' FROM DUAL; EXIT; "@ } # Utilizator comun => se modifica din root, al carui serviciu poarta # numele bazei. Reutilizam lista construita pentru LOCAL_LISTENER. $sysPwdPus = $false foreach ($srv in $serviciiDeIncercat) { $sysOut = Invoke-SqlPlus -OracleHome $oraHome -ServiceName $srv -DbHost $DbHost -Port $Port ` -Username "SYSTEM" -Password $SystemPassword -SqlCommand $sysPwdSql -Silent if ($sysOut -match 'SYS_PWD_DONE') { Write-LogSuccess "SYSTEM a primit verificator 10G (parola nu se schimba, doar se rescrie)" $sysPwdPus = $true break } Write-LogDebug "Rescrierea parolei SYSTEM prin serviciul ${srv}: $sysOut" } if (-not $sysPwdPus) { Write-LogWarning "Nu am putut rescrie parola lui SYSTEM. Conturile fara verificator 10G dau ORA-01017 din Instant Client 10/11." } } # Summary Write-LogSection "Setup Complete" Write-LogSuccess "Database initial setup completed successfully!" Write-Host "" Write-Log "Summary:" Write-Log " Oracle Home: $oraHome" Write-Log " Service Name: $ServiceName" Write-Log " Oracle Version: $($versionInfo.Version)" Write-Log " Container: $($containerInfo.ContainerName)" Write-Log " Tablespace ROA: $DatafileDir\TS_ROA.DBF" Write-Log " User: CONTAFIN_ORACLE" Write-Log " Data Pump Dir: DMPDIR -> $dmpDir" if (-not $SkipSqlnetConfig) { Write-Log " Old Client Auth: Enabled (sqlnet.ora + 10G verifier)" } Write-Host "" Write-Log "Next steps:" Write-Log " 1. Run 02-create-sys-objects.ps1 to install SYS objects" Write-Log " 2. Run 03-import-contafin.ps1 to import CONTAFIN_ORACLE schema" Close-LogFile -Success $true exit 0 } catch { Write-LogError "Setup failed: $_" Write-LogError $_.ScriptStackTrace Close-LogFile -Success $false exit 1 }