490 lines
25 KiB
PowerShell
490 lines
25 KiB
PowerShell
# ================================================================================
|
|
# 파일: 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 |