Files
adusermanager/Scripts/UI-Tab-AddUser.ps1

490 lines
25 KiB
PowerShell
Raw Normal View History

2025-09-15 13:46:19 +09:00
# ================================================================================
# 파일: Scripts/UI-Tab-AddUser.ps1
# 역할: '계정 생성' 탭 UI 및 기능 구현
#
# 작성자: 양범진
# 버전: 1.13
# 생성일자: 2025-06-05
# 최종 수정일자: 2025-06-12
#
# 설명:
# - '계정 생성' 탭의 UI 컨트롤들을 생성하고 배치
# - 사용자 정보 입력, OU 선택, 라이선스 할당 등 계정 생성과 관련된 모든 로직 처리
# - 코드 가독성 및 유지보수 편의성을 위해 전체적으로 포매팅 및 주석 재정리
# ================================================================================
#region '계정 생성' 탭 UI 초기화 함수
# --- '계정 생성' 탭의 모든 UI 컨트롤을 생성하고 배치하는 메인 함수 ---
function Initialize-AddUserTab {
Param([System.Windows.Forms.TabPage]$parentTab)
# 메인 레이아웃: 전체 탭을 2열 3행으로 구성
$addTabMainLayout = New-Object System.Windows.Forms.TableLayoutPanel
$addTabMainLayout.Dock = "Fill"
$addTabMainLayout.Padding = [System.Windows.Forms.Padding](10)
$addTabMainLayout.ColumnCount = 2
$addTabMainLayout.RowCount = 3
$addTabMainLayout.ColumnStyles.Add((New-Object System.Windows.Forms.ColumnStyle([System.Windows.Forms.SizeType]::Percent, 50))) | Out-Null
$addTabMainLayout.ColumnStyles.Add((New-Object System.Windows.Forms.ColumnStyle([System.Windows.Forms.SizeType]::Percent, 50))) | Out-Null
$addTabMainLayout.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Absolute, 80))) | Out-Null
$addTabMainLayout.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Percent, 100))) | Out-Null
$addTabMainLayout.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Absolute, 60))) | Out-Null
$parentTab.Controls.Add($addTabMainLayout) | Out-Null
# 상단: 작업 모드 선택 (AD/Azure AD 동기화 vs On-Premise AD만 생성)
$groupMode = New-Object System.Windows.Forms.GroupBox
$groupMode.Text = "작업 모드 선택"
$groupMode.Dock = "Fill"
$addTabMainLayout.Controls.Add($groupMode, 0, 0) | Out-Null
$addTabMainLayout.SetColumnSpan($groupMode, 2)
$script:add_radioSync = New-Object System.Windows.Forms.RadioButton
$script:add_radioSync.Text = "AD/Azure AD 동기화 및 라이선스 할당"
$script:add_radioSync.Location = New-Object System.Drawing.Point(25, 30)
$script:add_radioSync.AutoSize = $true
$script:add_radioSync.Checked = $true
$groupMode.Controls.Add($script:add_radioSync) | Out-Null
$script:add_radioOnPremOnly = New-Object System.Windows.Forms.RadioButton
$script:add_radioOnPremOnly.Text = "온프레미스 AD에만 생성"
$script:add_radioOnPremOnly.Location = New-Object System.Drawing.Point(450, 30)
$script:add_radioOnPremOnly.AutoSize = $true
$groupMode.Controls.Add($script:add_radioOnPremOnly) | Out-Null
# 왼쪽 패널: 사용자 정보 입력 및 OU 선택 영역
$leftPanelLayout = New-Object System.Windows.Forms.TableLayoutPanel
$leftPanelLayout.Dock = "Fill"
$leftPanelLayout.RowCount = 2
$leftPanelLayout.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Absolute, 180))) | Out-Null
$leftPanelLayout.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Percent, 100))) | Out-Null
$addTabMainLayout.Controls.Add($leftPanelLayout, 0, 1) | Out-Null
# 왼쪽 패널 (상단): 사용자 정보 입력 그룹박스
$groupUserInfo = New-Object System.Windows.Forms.GroupBox
$groupUserInfo.Text = "1. 사용자 정보"
$groupUserInfo.Dock = "Fill"
$leftPanelLayout.Controls.Add($groupUserInfo, 0, 0) | Out-Null
$userInfoLayout = New-Object System.Windows.Forms.TableLayoutPanel
$userInfoLayout.Dock = "Fill"
$userInfoLayout.Padding = [System.Windows.Forms.Padding](10)
$userInfoLayout.ColumnCount = 2
$userInfoLayout.RowCount = 4
$userInfoLayout.ColumnStyles.Add((New-Object System.Windows.Forms.ColumnStyle([System.Windows.Forms.SizeType]::Absolute, 110))) | Out-Null
$userInfoLayout.ColumnStyles.Add((New-Object System.Windows.Forms.ColumnStyle([System.Windows.Forms.SizeType]::Percent, 100))) | Out-Null
$rowHeight = 35
$userInfoLayout.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Absolute, $rowHeight))) | Out-Null
$userInfoLayout.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Absolute, $rowHeight))) | Out-Null
$userInfoLayout.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Absolute, $rowHeight))) | Out-Null
$userInfoLayout.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Absolute, $rowHeight))) | Out-Null
$groupUserInfo.Controls.Add($userInfoLayout) | Out-Null
# 사용자 정보 입력 컨트롤들 (한글 성/이름, 영문 계정명, 비밀번호)
$labelLastNameKr = New-Object System.Windows.Forms.Label
$labelLastNameKr.Text = "한글 성:"
$labelLastNameKr.Dock = "Fill"
$labelLastNameKr.TextAlign = "MiddleLeft"
$script:add_textBoxLastNameKr = New-Object System.Windows.Forms.TextBox
$script:add_textBoxLastNameKr.Dock = "Fill"
$labelFirstNameKr = New-Object System.Windows.Forms.Label
$labelFirstNameKr.Text = "한글 이름:"
$labelFirstNameKr.Dock = "Fill"
$labelFirstNameKr.TextAlign = "MiddleLeft"
$script:add_textBoxFirstNameKr = New-Object System.Windows.Forms.TextBox
$script:add_textBoxFirstNameKr.Dock = "Fill"
$labelAccountNameEn = New-Object System.Windows.Forms.Label
$labelAccountNameEn.Text = "영문 계정명:"
$labelAccountNameEn.Dock = "Fill"
$labelAccountNameEn.TextAlign = "MiddleLeft"
# 영문 계정명 입력란 + 유효성 검사 아이콘 패널
$accountNamePanel = New-Object System.Windows.Forms.TableLayoutPanel
$accountNamePanel.Dock = "Fill"
$accountNamePanel.ColumnCount = 2
$accountNamePanel.ColumnStyles.Add((New-Object System.Windows.Forms.ColumnStyle([System.Windows.Forms.SizeType]::Percent, 100))) | Out-Null
$accountNamePanel.ColumnStyles.Add((New-Object System.Windows.Forms.ColumnStyle([System.Windows.Forms.SizeType]::Absolute, 22))) | Out-Null
$accountNamePanel.Margin = [System.Windows.Forms.Padding](0)
$script:add_textBoxAccountNameEn = New-Object System.Windows.Forms.TextBox
$script:add_textBoxAccountNameEn.Dock = "Fill"
$script:add_pictureBoxAccountValidation = New-Object System.Windows.Forms.PictureBox
$script:add_pictureBoxAccountValidation.Dock = "Fill"
$script:add_pictureBoxAccountValidation.SizeMode = "CenterImage"
$script:add_pictureBoxAccountValidation.Visible = $false
$script:add_toolTip = New-Object System.Windows.Forms.ToolTip
$accountNamePanel.Controls.AddRange(@($script:add_textBoxAccountNameEn, $script:add_pictureBoxAccountValidation)) | Out-Null
$labelPassword = New-Object System.Windows.Forms.Label
$labelPassword.Text = "초기 비밀번호:"
$labelPassword.Dock = "Fill"
$labelPassword.TextAlign = "MiddleLeft"
$script:add_textBoxPassword = New-Object System.Windows.Forms.TextBox
$script:add_textBoxPassword.Dock = "Fill"
$script:add_textBoxPassword.Text = $script:Configuration.DefaultPassword
$userInfoLayout.Controls.Add($labelLastNameKr, 0, 0) | Out-Null
$userInfoLayout.Controls.Add($script:add_textBoxLastNameKr, 1, 0) | Out-Null
$userInfoLayout.Controls.Add($labelFirstNameKr, 0, 1) | Out-Null
$userInfoLayout.Controls.Add($script:add_textBoxFirstNameKr, 1, 1) | Out-Null
$userInfoLayout.Controls.Add($labelAccountNameEn, 0, 2) | Out-Null
$userInfoLayout.Controls.Add($accountNamePanel, 1, 2) | Out-Null
$userInfoLayout.Controls.Add($labelPassword, 0, 3) | Out-Null
$userInfoLayout.Controls.Add($script:add_textBoxPassword, 1, 3) | Out-Null
# 왼쪽 패널 (하단): OU 선택 그룹박스
$groupOU = New-Object System.Windows.Forms.GroupBox
$groupOU.Text = "2. 대상 OU 선택"
$groupOU.Dock = "Fill"
$leftPanelLayout.Controls.Add($groupOU, 0, 1) | Out-Null
$ouLayout = New-Object System.Windows.Forms.TableLayoutPanel
$ouLayout.Dock = "Fill"
$ouLayout.Padding = [System.Windows.Forms.Padding](10)
$ouLayout.ColumnCount = 2
$ouLayout.RowCount = 3
$ouLayout.ColumnStyles.Add((New-Object System.Windows.Forms.ColumnStyle([System.Windows.Forms.SizeType]::Percent, 100))) | Out-Null
$ouLayout.ColumnStyles.Add((New-Object System.Windows.Forms.ColumnStyle([System.Windows.Forms.SizeType]::Absolute, 40))) | Out-Null
$ouLayout.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Absolute, 35))) | Out-Null
$ouLayout.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Percent, 100))) | Out-Null
$ouLayout.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Absolute, 25))) | Out-Null
$groupOU.Controls.Add($ouLayout) | Out-Null
# OU 선택 컨트롤들 (선택된 OU 표시, 새로고침 버튼, TreeView, 동기화 상태 라벨)
$script:add_textBoxSelectedOU = New-Object System.Windows.Forms.TextBox
$script:add_textBoxSelectedOU.Dock = "Fill"
$script:add_textBoxSelectedOU.ReadOnly = $true
$script:add_textBoxSelectedOU.Text = "아래에서 OU를 선택하세요."
$buttonRefreshOUs = New-Object System.Windows.Forms.Button
$buttonRefreshOUs.Text = "🔄"
$buttonRefreshOUs.Dock = "Fill"
$buttonRefreshOUs.Font = New-Object System.Drawing.Font("Segoe UI Emoji", 10)
$script:add_treeViewOUs = New-Object System.Windows.Forms.TreeView
$script:add_treeViewOUs.Dock = "Fill"
$script:add_treeViewOUs.HideSelection = $false
$script:add_labelOUSyncStatus = New-Object System.Windows.Forms.Label
$script:add_labelOUSyncStatus.Dock = "Fill"
$script:add_labelOUSyncStatus.TextAlign = "MiddleLeft"
$script:add_labelOUSyncStatus.Text = "OU를 선택하면 동기화 상태를 안내합니다."
$ouLayout.Controls.Add($script:add_textBoxSelectedOU, 0, 0) | Out-Null
$ouLayout.Controls.Add($buttonRefreshOUs, 1, 0) | Out-Null
$ouLayout.Controls.Add($script:add_treeViewOUs, 0, 1) | Out-Null
$ouLayout.SetColumnSpan($script:add_treeViewOUs, 2)
$ouLayout.Controls.Add($script:add_labelOUSyncStatus, 0, 2) | Out-Null
$ouLayout.SetColumnSpan($script:add_labelOUSyncStatus, 2)
# 오른쪽 패널: M365 라이선스 및 서비스 플랜 할당 영역
$script:add_groupLicense = New-Object System.Windows.Forms.GroupBox
$script:add_groupLicense.Text = "3. M365 라이선스 및 서비스 플랜 할당"
$script:add_groupLicense.Dock = "Fill"
$addTabMainLayout.Controls.Add($script:add_groupLicense, 1, 1) | Out-Null
$licenseLayout = New-Object System.Windows.Forms.TableLayoutPanel
$licenseLayout.Dock = "Fill"
$licenseLayout.Padding = [System.Windows.Forms.Padding](10)
$licenseLayout.RowCount = 2
$licenseLayout.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Percent, 50))) | Out-Null
$licenseLayout.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Percent, 50))) | Out-Null
$script:add_groupLicense.Controls.Add($licenseLayout) | Out-Null
# 라이선스 목록 및 서비스 플랜 목록 컨트롤
$script:add_listBoxLicenses = New-Object System.Windows.Forms.ListBox
$script:add_listBoxLicenses.Dock = "Fill"
$script:add_checkedListBoxServicePlans = New-Object System.Windows.Forms.CheckedListBox
$script:add_checkedListBoxServicePlans.Dock = "Fill"
$script:add_checkedListBoxServicePlans.CheckOnClick = $true
$licenseLayout.Controls.Add($script:add_listBoxLicenses, 0, 0) | Out-Null
$licenseLayout.Controls.Add($script:add_checkedListBoxServicePlans, 0, 1) | Out-Null
# 하단: 계정 생성 실행 버튼
$script:add_buttonCreate = New-Object System.Windows.Forms.Button
$script:add_buttonCreate.Text = "계정 생성 실행(&R)"
$script:add_buttonCreate.Dock = "Fill"
$script:add_buttonCreate.Font = New-Object System.Drawing.Font("Segoe UI", 12, [System.Drawing.FontStyle]::Bold)
$addTabMainLayout.Controls.Add($script:add_buttonCreate, 0, 2) | Out-Null
$addTabMainLayout.SetColumnSpan($script:add_buttonCreate, 2)
# 이벤트 핸들러 연결
$script:add_radioSync.add_CheckedChanged($add_ModeChangedHandler)
$script:add_radioOnPremOnly.add_CheckedChanged($add_ModeChangedHandler)
$script:add_textBoxAccountNameEn.Add_TextChanged($add_AccountNameEn_TextChanged)
$script:add_textBoxAccountNameEn.Add_Leave($add_AccountNameEn_Leave)
$buttonRefreshOUs.Add_Click({ Update-OU-TreeView -TriggerControl $buttonRefreshOUs })
$script:add_treeViewOUs.Add_AfterSelect($add_treeViewOUs_AfterSelect)
$script:add_listBoxLicenses.Add_SelectedIndexChanged($add_listBoxLicenses_SelectedIndexChanged)
$script:add_buttonCreate.Add_Click($add_buttonCreate_Click)
}
#endregion
#region '계정 생성' 탭 이벤트 핸들러 및 함수
# --- OU TreeView를 로드 및 갱신하는 함수 ---
function Update-OU-TreeView {
Param($TriggerControl)
$allOUs = Invoke-Synchronous -TriggerControl $TriggerControl -StatusMessage "[계정 생성] OU 목록을 조회합니다..." -ScriptBlock {
Get-ADOrganizationalUnit -Filter * -SearchBase $script:CurrentADDomainDN -SearchScope Subtree -Server $script:Configuration.OnPremDomainController -ErrorAction SilentlyContinue
}
if ($allOUs) {
# OU 목록을 부모-자식 관계의 해시테이블로 재구성
$ouHierarchy = @{}
foreach ($ou in $allOUs) {
$parentDN = $ou.DistinguishedName -replace '^OU=[^,]+,', ''
if (-not $ouHierarchy.ContainsKey($parentDN)) {
$ouHierarchy[$parentDN] = [System.Collections.ArrayList]::new()
}
$ouHierarchy[$parentDN].Add($ou) | Out-Null
}
# TreeView 업데이트 시작 (UI 깜빡임 방지)
$script:add_treeViewOUs.BeginUpdate()
$script:add_treeViewOUs.Nodes.Clear()
# 최상위 루트 노드 추가
$rootNode = New-Object System.Windows.Forms.TreeNode($script:CurrentADDomainInfo.DnsRoot)
$rootNode.Tag = $script:CurrentADDomainDN
$script:add_treeViewOUs.Nodes.Add($rootNode) | Out-Null
# 재귀 함수를 통해 OU 계층 구조 빌드
Build-OU-TreeView -ParentDN $script:CurrentADDomainDN -ParentUiNode $rootNode -OUHierarchy $ouHierarchy
$rootNode.Expand()
# 로드 완료 후 선택된 노드 해제
if ($script:add_treeViewOUs.SelectedNode) {
$script:add_treeViewOUs.SelectedNode = $null
}
$script:add_treeViewOUs.EndUpdate()
Write-Log "[계정 생성] OU 목록 로드/갱신 완료."
}
}
# --- 작업 모드 변경 이벤트 핸들러 ---
# 라디오 버튼 선택에 따라 라이선스 할당 영역의 활성화 여부와 실행 버튼 텍스트 변경
$add_ModeChangedHandler = {
param($src, $e)
if (-not $src.Checked) { return }
$isSyncMode = $script:add_radioSync.Checked
$script:add_groupLicense.Enabled = $isSyncMode
if ($isSyncMode) {
$script:add_buttonCreate.Text = "계정 생성 및 할당 실행(&R)"
}
else {
$script:add_buttonCreate.Text = "온프레미스 계정 생성 실행(&R)"
}
}
# --- 영문 계정명 텍스트 변경 이벤트 핸들러 ---
# 사용자가 계정명을 입력할 때 유효성 검사 아이콘을 '확인 중' 상태로 표시
$add_AccountNameEn_TextChanged = {
$accountName = $script:add_textBoxAccountNameEn.Text
if ([string]::IsNullOrWhiteSpace($accountName)) {
$script:add_pictureBoxAccountValidation.Visible = $false
return
}
$script:add_pictureBoxAccountValidation.Image = $script:iconGreen
$script:add_toolTip.SetToolTip($script:add_pictureBoxAccountValidation, "확인 중...")
$script:add_pictureBoxAccountValidation.Visible = $true
}
# --- 영문 계정명 입력란 포커스 잃음 이벤트 핸들러 ---
# 입력이 완료되면 계정명의 유효성과 중복 여부를 검사하고 결과를 아이콘과 툴팁으로 표시
$add_AccountNameEn_Leave = {
$accountName = $script:add_textBoxAccountNameEn.Text
if ([string]::IsNullOrWhiteSpace($accountName)) {
$script:add_pictureBoxAccountValidation.Visible = $false
return
}
# Azure AD 동기화 모드일 때만 Azure AD 중복 확인을 수행
$isSyncMode = $script:add_radioSync.Checked
$validationResult = Test-SamAccountName -AccountName $accountName -CheckAzureAD:$isSyncMode
if ($validationResult.IsValid) {
$script:add_pictureBoxAccountValidation.Image = $script:iconGreen
}
else {
$script:add_pictureBoxAccountValidation.Image = $script:iconRed
}
$script:add_toolTip.SetToolTip($script:add_pictureBoxAccountValidation, $validationResult.Reason)
$script:add_pictureBoxAccountValidation.Visible = $true
}
# --- OU TreeView 노드 선택 이벤트 핸들러 ---
# OU를 선택하면 해당 OU의 DN을 텍스트박스에 표시하고, 동기화 대상 여부를 확인하여 상태 라벨에 표시
$add_treeViewOUs_AfterSelect = {
param($source, $e)
if ($e.Node -ne $null -and $e.Node.Tag) {
$selectedOUDN = $e.Node.Tag.ToString()
# 루트 도메인은 선택 불가 처리
if ($selectedOUDN -eq $script:CurrentADDomainDN) {
$script:add_textBoxSelectedOU.Text = "OU를 선택해야 합니다."
$script:add_labelOUSyncStatus.Text = "루트 도메인은 선택할 수 없습니다."
$script:add_labelOUSyncStatus.ForeColor = [System.Drawing.Color]::Red
return
}
$script:add_textBoxSelectedOU.Text = $selectedOUDN
$isSynchronized = $false
# 설정된 동기화 OU 목록과 비교
foreach ($syncRootOU in $script:SynchronizedOURoots) {
if ($selectedOUDN -eq $syncRootOU -or $selectedOUDN.EndsWith(",$syncRootOU", "OrdinalIgnoreCase")) {
$isSynchronized = $true
break
}
}
if ($isSynchronized) {
$script:add_labelOUSyncStatus.Text = "선택된 OU는 동기화 대상입니다."
$script:add_labelOUSyncStatus.ForeColor = [System.Drawing.Color]::Green
}
else {
$script:add_labelOUSyncStatus.Text = "주의: 선택된 OU는 동기화 대상이 아닐 수 있습니다!"
$script:add_labelOUSyncStatus.ForeColor = [System.Drawing.Color]::Red
}
}
}
# --- 라이선스 목록 선택 이벤트 핸들러 ---
# 라이선스를 선택하면 해당 라이선스에 포함된 서비스 플랜 목록을 가져와 아래쪽 체크리스트에 표시
$add_listBoxLicenses_SelectedIndexChanged = {
$script:add_checkedListBoxServicePlans.Items.Clear()
$selectedItem = $script:add_listBoxLicenses.SelectedItem
# "(라이선스 할당 안함)"을 선택했거나 선택된 항목이 없으면 함수 종료
if ($null -eq $selectedItem -or $null -eq $selectedItem.SkuId) {
return
}
Write-Log "'$($selectedItem.DisplayName)' 라이선스의 서비스 플랜을 조회합니다."
$allPlans = $selectedItem.SkuObject.ServicePlans | Sort-Object ServicePlanName
foreach ($plan in $allPlans) {
# 모든 플랜을 기본적으로 체크된 상태로 추가
$script:add_checkedListBoxServicePlans.Items.Add($plan, $true) | Out-Null
}
$script:add_checkedListBoxServicePlans.DisplayMember = "ServicePlanName"
}
# --- '계정 생성 실행' 버튼 클릭 이벤트 핸들러 ---
# 입력된 정보를 바탕으로 실제 계정 생성 작업을 수행하는 메인 로직
$add_buttonCreate_Click = {
# 1. 필수 값 확인
$lastNameKr = $script:add_textBoxLastNameKr.Text.Trim()
$firstNameKr = $script:add_textBoxFirstNameKr.Text.Trim()
$accountNameEn = $script:add_textBoxAccountNameEn.Text.Trim()
$targetOUDN = $script:add_textBoxSelectedOU.Text.Trim()
$passwordInput = $script:add_textBoxPassword.Text
$isSyncMode = $script:add_radioSync.Checked
if (-not ($lastNameKr -and $firstNameKr -and $accountNameEn -and $passwordInput)) {
[System.Windows.Forms.MessageBox]::Show("사용자 정보(한글 성/이름, 영문 계정명)와 비밀번호는 필수 입력 항목입니다.", "입력 오류", "OK", "Warning") | Out-Null
return
}
if ($targetOUDN -eq "아래에서 OU를 선택하세요." -or $targetOUDN -eq "OU를 선택해야 합니다." -or [string]::IsNullOrWhiteSpace($targetOUDN)) {
[System.Windows.Forms.MessageBox]::Show("대상 OU를 선택해야 합니다.", "입력 오류", "OK", "Warning") | Out-Null
return
}
# 생성 직전 최종 유효성 검사 (Azure AD 중복 확인 포함)
$validationResult = Test-SamAccountName -AccountName $accountNameEn -CheckAzureAD:$isSyncMode
if (-not $validationResult.IsValid) {
$message = "계정명 유효성 검사를 통과하지 못했습니다.`n사유: $($validationResult.Reason)"
[System.Windows.Forms.MessageBox]::Show($message, "입력 오류", "OK", "Warning") | Out-Null
return
}
# 2. 메인 작업 실행 (Invoke-Synchronous 래퍼 함수 사용)
$success = Invoke-Synchronous -TriggerControl $this -StatusMessage "계정 생성 작업을 실행합니다..." -ScriptBlock {
# 2.1. On-Premise AD 사용자 생성
$userPrincipalName = "$accountNameEn@$($script:Configuration.UPNSuffix)"
$commonName = "$lastNameKr$firstNameKr [$accountNameEn]"
Write-Log "온프레미스 AD 사용자 생성을 시작합니다: $accountNameEn"
New-ADUser -SamAccountName $accountNameEn `
-UserPrincipalName $userPrincipalName `
-Name $commonName `
-DisplayName $commonName `
-GivenName $firstNameKr `
-Surname $lastNameKr `
-Path $targetOUDN `
-AccountPassword (ConvertTo-SecureString $passwordInput -AsPlainText -Force) `
-Enabled $true `
-ChangePasswordAtLogon $false `
-EmailAddress $userPrincipalName `
-Server $script:Configuration.OnPremDomainController `
-PassThru `
-ErrorAction Stop
Write-Log "온프레미스 AD 사용자 '$accountNameEn' 생성 성공."
if (-not $isSyncMode) { return $true } # On-Premise 전용 모드일 경우 여기서 작업 종료
# 2.2. 동기화 실행 및 Azure AD에서 사용자 확인
Invoke-AadConnectSync -PolicyType Delta
Write-Log "Azure AD에서 사용자 '$userPrincipalName' 확인 시도 (최대 120초 대기)..."
$azureAdUser = $null
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
while ($stopwatch.Elapsed.TotalSeconds -lt 120) {
Write-Log "Azure AD에서 사용자 확인 중... ($([math]::Round($stopwatch.Elapsed.TotalSeconds))초 경과)"
$azureAdUser = Get-MgUser -Filter "userPrincipalName eq '$userPrincipalName'" -ErrorAction SilentlyContinue
if ($azureAdUser) { break }
Start-Sleep -Seconds 10
}
$stopwatch.Stop()
if (-not $azureAdUser) { throw "제한 시간(120초) 내에 Azure AD에서 사용자를 찾지 못했습니다." }
$elapsedSeconds = [math]::Round($stopwatch.Elapsed.TotalSeconds)
Write-Log "사용자 '$userPrincipalName'을(를) Azure AD에서 찾았습니다! (소요 시간: ${elapsedSeconds}초)" -Level SUCCESS
# 2.3. 사용 위치(Usage Location) 및 라이선스 할당
Write-Log "사용 위치 설정 중: $($script:Configuration.DefaultUsageLocation)"
Update-MgUser -UserId $azureAdUser.UserPrincipalName -UsageLocation $script:Configuration.DefaultUsageLocation -ErrorAction Stop
$selectedLicenseItem = $script:add_listBoxLicenses.SelectedItem
if ($selectedLicenseItem -and $selectedLicenseItem.SkuId) {
Write-Log "라이선스 할당 중: $($selectedLicenseItem.DisplayName)"
$disabledPlans = [System.Collections.Generic.List[string]]::new()
# 체크 해제된 서비스 플랜 목록을 구성
for ($i = 0; $i -lt $script:add_checkedListBoxServicePlans.Items.Count; $i++) {
if (-not $script:add_checkedListBoxServicePlans.GetItemChecked($i)) {
$servicePlanId = $script:add_checkedListBoxServicePlans.Items[$i].ServicePlanId
$disabledPlans.Add($servicePlanId)
}
}
# 라이선스 할당 API 호출
$licenseBody = @{
AddLicenses = @(@{SkuId = $selectedLicenseItem.SkuId; DisabledPlans = $disabledPlans})
RemoveLicenses = @()
}
Set-MgUserLicense -UserId $azureAdUser.UserPrincipalName -BodyParameter $licenseBody -ErrorAction Stop
Write-Log "라이선스 및 서비스 플랜 할당 완료."
}
return $true
} -RequiresAzureAD:$isSyncMode
# 3. 최종 결과 처리
if ($success) {
[System.Windows.Forms.MessageBox]::Show("모든 작업이 성공적으로 완료되었습니다.", "작업 완료", "OK", "Information") | Out-Null
Reset-AddUserTab
}
}
#endregion
Write-Host "UI-Tab-AddUser.ps1 로드 완료." -ForegroundColor Cyan