Platform
Azure CLI/PowerShell과 SQL Server 운영 자동화 흐름
Azure CLI와 PowerShell로 VM, 네트워크, NSG를 반복 가능하게 구성하고 SQL Server 배포와 Azure SQL 마이그레이션을 운영 관점으로 정리합니다.

Azure 포털은 리소스를 처음 이해하기에 좋다. 어떤 설정이 어디에 있는지 눈으로 확인할 수 있고, VM, VNet, NSG, Public IP, Disk 같은 리소스의 관계를 빠르게 따라갈 수 있다. 하지만 같은 구성을 여러 번 만들거나, 실습 환경을 정리하고 다시 만들거나, 운영 환경 변경 이력을 남기려면 포털 클릭만으로는 부족하다.
Azure CLI와 PowerShell을 함께 정리한 이유는 이 지점 때문이다. 둘 다 Azure 리소스를 코드처럼 다룰 수 있게 해준다. CLI는 shell script와 CI/CD에서 쓰기 좋고, PowerShell은 Windows 관리 작업과 Azure 리소스 조작을 같은 문맥에서 다루기 좋다. 여기에 SQL Server VM 구성과 Azure SQL 마이그레이션까지 연결하면 “리소스를 만드는 법”에서 “운영 가능한 데이터 서비스 환경을 준비하는 법”으로 관점이 바뀐다.
CLI 작업은 리소스 의존성을 드러낸다
Azure VM 하나를 만들 때도 실제로는 여러 리소스가 함께 필요하다. Resource Group, VNet, Subnet, Public IP, NSG, NIC, VM이 순서대로 연결된다. 포털에서는 한 화면에서 지나가지만, CLI로 나누어 만들면 의존성이 선명해진다.
az login
az account show
az group create \
--name rg-ubuntuvmkty \
--location koreacentral
az network vnet create \
--resource-group rg-ubuntuvmkty \
--name vnet-ubuntukty \
--address-prefix 10.0.0.0/16 \
--subnet-name snet-ubuntukty \
--subnet-prefix 10.0.1.0/24여기까지는 VM이 아니라 네트워크 경계를 만드는 단계다. VM을 먼저 떠올리기 쉽지만, 운영에서는 네트워크 설계가 먼저다. 어떤 주소 대역을 쓸지, 어떤 서브넷에 어떤 역할의 리소스를 둘지, 나중에 Bastion이나 Private Endpoint를 붙일 공간이 있는지 먼저 봐야 한다.
Public IP와 NSG는 외부 접속 경로를 만든다.
az network public-ip create \
--resource-group rg-ubuntuvmkty \
--name pip-ubuntukty \
--sku Standard \
--allocation-method Static
az network nsg create \
--resource-group rg-ubuntuvmkty \
--name nsg-ubuntukty
az network nsg rule create \
--resource-group rg-ubuntuvmkty \
--nsg-name nsg-ubuntukty \
--name AllowSSHFromMyIp \
--protocol Tcp \
--priority 1000 \
--source-address-prefixes 203.0.113.10/32 \
--destination-port-ranges 22 \
--access Allow실습에서는 SSH를 열어 확인하는 일이 많지만, 규칙 이름을 AllowSSH로 끝내기보다 AllowSSHFromMyIp처럼 의도를 남기는 편이 좋다. 운영에서 “22번 포트가 열려 있다”는 사실보다 “어디에서 오는 22번만 허용했는가”가 더 중요하다.
NIC는 네트워크 요소를 VM에 연결하는 지점이다.
az network nic create \
--resource-group rg-ubuntuvmkty \
--name nic-ubuntukty \
--vnet-name vnet-ubuntukty \
--subnet snet-ubuntukty \
--network-security-group nsg-ubuntukty \
--public-ip-address pip-ubuntuktyVM 생성은 마지막에 가깝다.
az vm create \
--resource-group rg-ubuntuvmkty \
--name vm-ubuntukty \
--nics nic-ubuntukty \
--image UbuntuLTS \
--size Standard_B2s \
--admin-username azureuser \
--authentication-type ssh \
--ssh-key-values ~/.ssh/id_rsa.pub \
--tags Dept=IT Project=Demo이 흐름을 script로 남기면 같은 환경을 다시 만들 수 있고, 어떤 값이 바뀌었는지 diff로 볼 수 있다. 더 나아가 Terraform/Bicep으로 옮기기 전 단계의 좋은 연습이 된다.
생성 후에는 “VM이 만들어졌다”에서 멈추지 않고 상태와 접속 경로를 확인한다.
az vm get-instance-view \
--resource-group rg-ubuntuvmkty \
--name vm-ubuntukty \
--query "instanceView.statuses[].displayStatus" \
--output table
az vm list-ip-addresses \
--resource-group rg-ubuntuvmkty \
--name vm-ubuntukty \
--output tableUbuntu VM이라면 SSH 접속을 확인하고, Windows VM이라면 RDP 경로를 확인한다. 다만 운영 환경에서는 public IP로 직접 접속하는 방식을 기본값으로 두지 않는 편이 좋다.
ssh azureuser@203.0.113.20practice
- Public IP + source IP restricted NSG
production preference
- Azure Bastion
- VPN or ExpressRoute
- private subnet
- no public management portCLI 작업을 반복 가능하게 만들려면 생성 명령과 확인 명령을 함께 둔다. 리소스 생성은 성공했지만 NSG가 잘못 붙었거나, NIC가 다른 subnet에 연결되었거나, public IP가 동적으로 만들어진 경우도 있다. 그래서 스크립트 마지막에는 현재 상태를 표 형태로 출력하게 두는 편이 좋다.
az resource list \
--resource-group rg-ubuntuvmkty \
--query "[].{name:name,type:type,location:location}" \
--output table이 출력은 단순 확인용이지만, 변경 전후를 비교할 때도 도움이 된다. Terraform처럼 plan/apply 모델을 쓰지 않는 CLI 스크립트일수록 “무엇이 만들어졌는지”를 명시적으로 남겨야 한다.
PowerShell은 Windows 운영 흐름과 잘 맞는다
PowerShell은 Windows VM, SQL Server, RDP, 관리 스크립트와 함께 쓸 때 자연스럽다. Azure 리소스를 만들면서 Windows 관리 명령까지 한 문맥에서 이어갈 수 있다.
$location = "KoreaCentral"
$rgName = "rg-myPowerShell"
$rg = New-AzResourceGroup `
-Name $rgName `
-Location $location
$subnet = New-AzVirtualNetworkSubnetConfig `
-Name "frontendSubnet" `
-AddressPrefix "10.0.0.0/24"
New-AzVirtualNetwork `
-Name "vnet-MyPSNet" `
-ResourceGroupName $rg.ResourceGroupName `
-Location $location `
-AddressPrefix "10.0.0.0/16" `
-Subnet $subnetPowerShell에서 중요한 점은 변수와 객체를 그대로 이어서 사용할 수 있다는 것이다. Resource Group을 만든 결과를 $rg에 담고, 그 속성을 다음 명령에 넘긴다. 반복 실행할 스크립트라면 Get-AzResourceGroup으로 존재 여부를 확인하고, 이미 있으면 재사용하거나 명시적으로 중단하는 방식이 좋다.
$existing = Get-AzResourceGroup -Name $rgName -ErrorAction SilentlyContinue
if ($null -eq $existing) {
New-AzResourceGroup -Name $rgName -Location $location
}삭제 명령은 특히 조심해야 한다. Resource Group 삭제는 그 안의 모든 리소스를 지운다. 실습 환경에서는 편리하지만, 운영에서는 태그와 잠금, 권한, 승인 절차가 필요하다.
# Remove-AzResourceGroup -Name rg-myPowerShell이런 명령은 스크립트에 남기더라도 기본적으로 주석 처리하고, 실수로 실행되지 않게 별도 cleanup 스크립트로 분리하는 편이 안전하다.
PowerShell은 로컬 Windows 관리 작업과 Azure 작업을 한 파일에서 다룰 수 있다는 장점이 있다. 예를 들어 로컬 디렉터리를 만들고, 설정 파일을 생성하고, Azure 리소스 정보를 조회한 뒤 배포 파라미터로 넘기는 흐름을 자연스럽게 연결할 수 있다.
$ErrorActionPreference = "Stop"
$workspace = "C:\ops\azure-lab"
if (-not (Test-Path $workspace)) {
New-Item -ItemType Directory -Path $workspace | Out-Null
}
$subscription = Get-AzContext
$subscription.Subscription.Name | Out-File "$workspace\subscription.txt"운영 스크립트에서는 입력값을 상단 변수로 모으고, 실행 중간에 사용자에게 중요한 값을 출력한다.
$location = "KoreaCentral"
$environment = "dev"
$owner = "platform"
Write-Host "Location: $location"
Write-Host "Environment: $environment"
Write-Host "Owner: $owner"이런 출력은 작은 차이처럼 보이지만, 여러 구독과 리소스 그룹을 오갈 때 실수를 줄인다. 특히 Azure 작업은 구독 context가 잘못 잡힌 상태에서 실행해도 명령 자체는 성공할 수 있기 때문에, Get-AzContext 확인을 습관처럼 넣는 것이 좋다.
SQL Server VM은 “설치 완료”보다 경계 설정이 중요하다
Azure Windows VM에 SQL Server를 올릴 때는 이미지 선택부터 운영 조건이 갈린다. SQL Server 포함 이미지를 선택하면 설치 과정은 줄어들지만 라이선스와 버전, 비용을 확인해야 한다. 일반 Windows Server 이미지에 직접 설치하면 선택권은 넓지만 설치, 패치, 서비스 계정, 포트, 방화벽 설정을 직접 챙겨야 한다.
VM OS: Windows Server 2019 or later
SQL Server: Developer / Express / Standard / Enterprise
VM size: at least 2 vCPU, 8 GB RAM for basic practice
OS disk: SSD
Data disk: separate managed disk for SQL data
Network: private subnet preferred
Access: Bastion or VPN preferred over public RDPSQL Server를 설치한 뒤에는 SSMS나 Azure Data Studio로 접속 테스트를 한다. 하지만 실제로 확인해야 할 것은 서비스 실행 여부만이 아니다.
SQL Server Configuration Manager
- SQL Server service running
- TCP/IP enabled
- port 1433 only if remote access is required
Windows Firewall
- allow TCP 1433 only when needed
Azure NSG
- avoid public 1433
- prefer private IP, VPN, Bastion, peering, or private endpoint실습에서는 외부에서 Public IP,1433으로 연결 테스트를 할 수 있다. 하지만 운영 환경에서 SQL Server 1433을 인터넷에 여는 것은 피하는 편이 좋다. 최소한 source IP를 제한해야 하고, 가능하면 private network 안에서만 접근하게 해야 한다.
Bad default
Internet -> VM public IP:1433 -> SQL Server
Preferred
Admin -> VPN/Bastion -> Private IP -> SQL Server
Application subnet -> Private IP:1433 -> SQL ServerSQL Server VM은 데이터 디스크도 분리해서 생각해야 한다. OS 디스크와 데이터 파일, 로그 파일, 백업 파일이 모두 같은 디스크에 있으면 IO 병목과 복구 문제가 커질 수 있다. 작은 실습에서는 단일 디스크로 충분해도, 구조를 정리할 때는 데이터/로그/백업 경계를 의식하는 것이 좋다.
C: OS and SQL binaries
F: data files
G: transaction logs
H: backup files운영에서 필요한 것은 설치 절차보다 복구 가능성이다. 백업이 어디에 저장되는지, 스냅샷과 SQL 백업의 차이를 이해하는지, 패치 후 되돌릴 수 있는지, 관리자 계정과 애플리케이션 계정을 분리했는지가 더 중요하다.
SQL Server VM을 운영 관점으로 보면 체크리스트는 다음처럼 바뀐다.
Compute
- VM size is matched to workload
- accelerated networking considered
- availability option selected when needed
Storage
- data and log disks separated when workload requires it
- backup target separated from primary data disk
- disk performance tier reviewed
Network
- SQL port is private by default
- RDP is not open to Internet
- NSG source ranges are explicit
Operations
- SQL backup job exists
- Windows patching policy exists
- SQL Server patching policy exists
- monitoring and alerting enabledSQL Server 접속 테스트도 단순 로그인 성공으로 끝내지 않는다. 애플리케이션 계정으로 필요한 DB에만 접근되는지, 불필요한 서버 권한이 없는지, 원격 접속이 어떤 네트워크 경로를 통해 들어오는지 확인해야 한다.
select
name,
type_desc,
is_disabled
from sys.server_principals
where type in ('S', 'U', 'G');백업은 “설정했다”보다 “복구해봤다”가 중요하다. 개발/스테이징 환경에서 작은 DB라도 restore 절차를 반복해보면, 백업 위치, 권한, 파일명 규칙, 복구 시간이 실제로 보인다.
Azure SQL 마이그레이션은 사전 평가가 절반이다
Azure Data Studio의 Azure SQL Migration 확장은 SQL Server를 Azure SQL Database나 Azure SQL Managed Instance로 옮기기 전에 평가와 계획을 돕는다. 마이그레이션은 “데이터 복사”만으로 끝나지 않는다. 호환성, 사용 중인 기능, 다운타임 허용 범위, 네트워크 연결, 인증 방식, 애플리케이션 connection string 변경까지 함께 봐야 한다.
Migration assessment
- unsupported SQL features
- compatibility level
- database size
- schema complexity
- linked servers, jobs, CLR, cross-database queries
- downtime tolerance
- network path to Azure대상도 구분해야 한다. Azure SQL Database는 관리 부담이 적고 PaaS에 가깝지만, 인스턴스 수준 기능이 필요한 경우 제약이 있을 수 있다. Azure SQL Managed Instance는 SQL Server와의 호환성이 더 높지만 네트워크 구성과 비용이 더 커질 수 있다.
Azure SQL Database
- database-level PaaS
- easier management
- strong fit for new cloud-native apps
Azure SQL Managed Instance
- higher SQL Server compatibility
- useful for lift-and-shift
- VNet integration and instance-level features온라인 마이그레이션과 오프라인 마이그레이션도 다르게 봐야 한다. 오프라인은 단순하지만 downtime이 생긴다. 온라인은 동기화와 cutover 계획이 필요하다. 어떤 방식을 택하든 마이그레이션 전 백업과 rollback 계획은 별도로 준비해야 한다.
마이그레이션 후에는 애플리케이션만 정상 실행되는지 보지 않는다. 느려진 쿼리, 누락된 인덱스, collation 차이, connection pool 설정, timeout, deadlock, DTU/vCore 사용률을 함께 본다.
select
object_name(object_id) as table_name,
name as index_name,
type_desc
from sys.indexes
where object_id > 0
order by table_name, index_id;cutover 단계에서는 애플리케이션 설정 변경이 핵심이다. DB 자체는 옮겼지만 connection string, firewall rule, managed identity, secret store, connection pool 설정이 맞지 않으면 애플리케이션은 실패한다.
Cutover checklist
1. source DB backup completed
2. migration validation completed
3. application maintenance window announced
4. connection string updated in secret store
5. firewall and private access confirmed
6. smoke test completed
7. monitoring dashboard watched
8. rollback condition agreed마이그레이션은 한 번의 이벤트처럼 보이지만, 실제로는 관측 기간이 필요하다. cutover 직후 1시간, 24시간, 1주일 동안 봐야 할 지표가 다르다. 직후에는 오류율과 연결 실패가 중요하고, 하루 정도 지나면 느린 쿼리와 리소스 사용률이 보이며, 일주일 정도 지나면 백업/유지관리/비용 패턴이 드러난다.
자동화 스크립트에는 운영 안전장치가 필요하다
CLI와 PowerShell로 리소스를 만들 수 있다는 것은 삭제와 변경도 쉽게 할 수 있다는 뜻이다. 그래서 스크립트에는 몇 가지 안전장치를 둔다.
set -euo pipefail
RESOURCE_GROUP="rg-ubuntuvmkty"
LOCATION="koreacentral"
az group show --name "$RESOURCE_GROUP" >/dev/null 2>&1 || \
az group create --name "$RESOURCE_GROUP" --location "$LOCATION"PowerShell에서도 error handling을 명시한다.
$ErrorActionPreference = "Stop"
try {
Get-AzResourceGroup -Name $rgName
} catch {
New-AzResourceGroup -Name $rgName -Location $location
}태그도 중요하다. 태그는 비용, 소유자, 환경, 서비스 등 운영 정보를 남기는 가장 간단한 방법이다.
az resource tag \
--ids "$RESOURCE_ID" \
--tags Environment=dev Owner=platform Team=backend CostCenter=lab리소스가 많아질수록 이름 규칙도 필요하다. rg-, vnet-, snet-, nsg-, pip-, nic-, vm- 같은 접두어를 쓰면 목록에서 역할을 빠르게 파악할 수 있다. 이름 규칙은 멋을 위한 것이 아니라 장애 중에 잘못된 리소스를 만지는 실수를 줄이기 위한 장치다.
정리
Azure CLI와 PowerShell은 포털을 대체하는 도구라기보다 운영 흐름을 명시적으로 남기는 도구에 가깝다. Resource Group, VNet, Subnet, Public IP, NSG, NIC, VM을 순서대로 만들면 리소스 간 의존성이 드러난다. Windows VM과 SQL Server를 구성할 때는 설치 성공보다 네트워크 경계, 디스크 분리, 계정 권한, 백업, 패치, 접속 경로가 더 중요해진다.
Azure SQL 마이그레이션도 같은 맥락이다. 데이터를 옮기기 전에 호환성과 기능 차이를 평가하고, 대상 서비스를 고르고, 네트워크와 인증 경로를 준비하고, cutover와 rollback을 계획해야 한다. 결국 클라우드 운영 자동화는 명령어를 많이 아는 일이 아니라, 반복 가능한 작업 안에 보안과 복구 가능성을 함께 넣는 일에 가깝다.
이 글의 무단 복제, 재배포, 자동 수집을 허용하지 않습니다. 인용이 필요한 경우 출처와 원문 링크를 함께 남겨주세요.