Penyediaan Sumber Azure dengan AZD
March 31, 2026 ยท View on GitHub
Navigasi Bab:
- ๐ Laman Utama Kursus: AZD Untuk Pemula
- ๐ Bab Semasa: Bab 4 - Infrastruktur sebagai Kod & Penyebaran
- โฌ ๏ธ Sebelumnya: Panduan Penyebaran
- โก๏ธ Bab Seterusnya: Bab 5: Penyelesaian AI Pelbagai Ejen
- ๐ง Berkaitan: Bab 6: Pengesahan Pra-Penyebaran
Pengenalan
Panduan menyeluruh ini merangkumi segala yang anda perlu tahu tentang penyediaan dan pengurusan sumber Azure menggunakan Azure Developer CLI. Pelajari cara melaksanakan corak Infrastruktur sebagai Kod (IaC) dari penciptaan sumber asas hingga seni bina infrastruktur tahap perusahaan yang maju menggunakan Bicep, templat ARM, Terraform, dan Pulumi.
Matlamat Pembelajaran
Dengan menyiapkan panduan ini, anda akan:
- Menguasai prinsip Infrastruktur sebagai Kod dan penyediaan sumber Azure
- Memahami pelbagai penyedia IaC yang disokong oleh Azure Developer CLI
- Merekabentuk dan melaksanakan templat Bicep untuk seni bina aplikasi biasa
- Mengkonfigurasi parameter sumber, pemboleh ubah, dan tetapan khusus persekitaran
- Melaksanakan corak infrastruktur maju termasuk rangkaian dan keselamatan
- Mengurus kitaran hidup sumber, kemas kini, dan penyelesaian pergantungan
Hasil Pembelajaran
Setelah selesai, anda akan dapat:
- Merekabentuk dan menyediakan infrastruktur Azure menggunakan Bicep dan templat ARM
- Mengkonfigurasi seni bina pelbagai perkhidmatan yang kompleks dengan pergantungan sumber yang betul
- Melaksanakan templat parameter untuk beberapa persekitaran dan konfigurasi
- Menyelesaikan masalah penyediaan infrastruktur dan memperbaiki kegagalan penyebaran
- Menerapkan prinsip Kerangka Kerja Arkitek Azure yang Baik pada reka bentuk infrastruktur
- Mengurus kemas kini infrastruktur dan melaksanakan strategi versi infrastruktur
Gambaran Keseluruhan Penyediaan Infrastruktur
Azure Developer CLI menyokong pelbagai penyedia Infrastruktur sebagai Kod (IaC):
- Bicep (disyorkan) - bahasa domain khusus Azure
- Templat ARM - templat Pengurus Sumber Azure berasaskan JSON
- Terraform - alat infrastruktur pelbagai awan
- Pulumi - infrastruktur moden sebagai kod dengan bahasa pengaturcaraan
Memahami Sumber Azure
Hierarki Sumber
Azure Account
โโโ Subscriptions
โโโ Resource Groups
โโโ Resources (App Service, Storage, Database, etc.)
Perkhidmatan Azure Biasa untuk Aplikasi
- Pengkomputeran: App Service, Container Apps, Functions, Mesin Maya
- Storan: Akaun Storan, Cosmos DB, Pangkalan Data SQL, PostgreSQL
- Rangkaian: Rangkaian Maya, Gateway Aplikasi, CDN
- Keselamatan: Key Vault, Application Insights, Log Analytics
- AI/ML: Perkhidmatan Kognitif, OpenAI, Pembelajaran Mesin
Templat Infrastruktur Bicep
Struktur Templat Bicep Asas
// infra/main.bicep
@description('The name of the environment')
param environmentName string
@description('The location for all resources')
param location string = resourceGroup().location
@description('The name of the application')
param applicationName string = 'myapp'
// Variables
var resourceToken = toLower(uniqueString(subscription().id, environmentName, location))
var tags = {
'azd-env-name': environmentName
'azd-app': applicationName
}
// Resource Group (created automatically by azd)
resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' existing = {
name: '${applicationName}-${environmentName}-rg'
}
// App Service Plan
resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = {
name: '${applicationName}-${environmentName}-plan'
location: location
tags: tags
sku: {
name: 'B1'
capacity: 1
}
properties: {
reserved: true // Linux App Service Plan
}
}
// Web App
resource webApp 'Microsoft.Web/sites@2022-03-01' = {
name: '${applicationName}-web-${resourceToken}'
location: location
tags: tags
properties: {
serverFarmId: appServicePlan.id
siteConfig: {
linuxFxVersion: 'NODE|18-lts'
alwaysOn: true
ftpsState: 'Disabled'
minTlsVersion: '1.2'
appSettings: [
{
name: 'WEBSITES_ENABLE_APP_SERVICE_STORAGE'
value: 'false'
}
{
name: 'NODE_ENV'
value: 'production'
}
]
}
httpsOnly: true
}
}
// Output values for azd
output WEB_URL string = 'https://${webApp.properties.defaultHostName}'
output WEB_NAME string = webApp.name
Corak Bicep Maju
Infrastruktur Modular
// infra/modules/app-service.bicep
@description('App Service configuration')
param name string
param location string
param planId string
param appSettings array = []
resource webApp 'Microsoft.Web/sites@2022-03-01' = {
name: name
location: location
properties: {
serverFarmId: planId
siteConfig: {
appSettings: appSettings
linuxFxVersion: 'NODE|18-lts'
alwaysOn: true
}
httpsOnly: true
}
}
output hostname string = webApp.properties.defaultHostName
output principalId string = webApp.identity.principalId
// infra/main.bicep - Using modules
module webAppModule 'modules/app-service.bicep' = {
name: 'webApp'
params: {
name: '${applicationName}-web-${resourceToken}'
location: location
planId: appServicePlan.id
appSettings: [
{
name: 'API_URL'
value: apiModule.outputs.endpoint
}
{
name: 'DATABASE_URL'
value: '@Microsoft.KeyVault(VaultName=${keyVault.name};SecretName=database-url)'
}
]
}
}
Penciptaan Sumber Bersyarat
@description('Whether to create a database')
param createDatabase bool = true
@description('Database SKU')
param databaseSku string = 'Basic'
resource database 'Microsoft.Sql/servers/databases@2021-11-01' = if (createDatabase) {
name: '${sqlServer.name}/${applicationName}-db'
location: location
sku: {
name: databaseSku
tier: databaseSku == 'Basic' ? 'Basic' : 'Standard'
}
properties: {
collation: 'SQL_Latin1_General_CP1_CI_AS'
}
}
๐๏ธ Penyediaan Pangkalan Data
Cosmos DB
resource cosmosAccount 'Microsoft.DocumentDB/databaseAccounts@2023-04-15' = {
name: '${applicationName}-cosmos-${resourceToken}'
location: location
tags: tags
kind: 'GlobalDocumentDB'
properties: {
databaseAccountOfferType: 'Standard'
locations: [
{
locationName: location
failoverPriority: 0
isZoneRedundant: false
}
]
capabilities: [
{
name: 'EnableServerless'
}
]
}
}
resource cosmosDatabase 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2023-04-15' = {
parent: cosmosAccount
name: '${applicationName}db'
properties: {
resource: {
id: '${applicationName}db'
}
}
}
resource todoContainer 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers@2023-04-15' = {
parent: cosmosDatabase
name: 'todos'
properties: {
resource: {
id: 'todos'
partitionKey: {
paths: ['/userId']
kind: 'Hash'
}
}
}
}
PostgreSQL
resource postgresServer 'Microsoft.DBforPostgreSQL/flexibleServers@2022-12-01' = {
name: '${applicationName}-postgres-${resourceToken}'
location: location
tags: tags
sku: {
name: 'Standard_B1ms'
tier: 'Burstable'
}
properties: {
version: '14'
administratorLogin: 'dbadmin'
administratorLoginPassword: databasePassword
storage: {
storageSizeGB: 32
}
backup: {
backupRetentionDays: 7
geoRedundantBackup: 'Disabled'
}
highAvailability: {
mode: 'Disabled'
}
}
}
resource postgresDatabase 'Microsoft.DBforPostgreSQL/flexibleServers/databases@2022-12-01' = {
parent: postgresServer
name: '${applicationName}db'
properties: {
charset: 'utf8'
collation: 'en_US.utf8'
}
}
// Allow Azure services to connect
resource firewallRule 'Microsoft.DBforPostgreSQL/flexibleServers/firewallRules@2022-12-01' = {
parent: postgresServer
name: 'AllowAzureServices'
properties: {
startIpAddress: '0.0.0.0'
endIpAddress: '0.0.0.0'
}
}
๐ Keselamatan dan Pengurusan Rahsia
Integrasi Key Vault
resource keyVault 'Microsoft.KeyVault/vaults@2023-02-01' = {
name: '${applicationName}-kv-${resourceToken}'
location: location
tags: tags
properties: {
sku: {
family: 'A'
name: 'standard'
}
tenantId: subscription().tenantId
enableRbacAuthorization: true
enableSoftDelete: true
softDeleteRetentionInDays: 7
}
}
// Grant Key Vault access to the web app
resource webAppKeyVaultAccess 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(keyVault.id, webApp.id, 'Key Vault Secrets User')
scope: keyVault
properties: {
roleDefinitionId: subscriptionResourceId(
'Microsoft.Authorization/roleDefinitions',
'4633458b-17de-408a-b874-0445c86b69e6' // Key Vault Secrets User
)
principalId: webApp.identity.principalId
principalType: 'ServicePrincipal'
}
}
// Store database connection string in Key Vault
resource databaseConnectionSecret 'Microsoft.KeyVault/vaults/secrets@2023-02-01' = {
parent: keyVault
name: 'database-connection-string'
properties: {
value: 'Server=${postgresServer.properties.fullyQualifiedDomainName};Database=${postgresDatabase.name};Port=5432;User Id=${postgresServer.properties.administratorLogin};Password=${databasePassword};'
}
}
Konfigurasi Identiti Terurus
resource webApp 'Microsoft.Web/sites@2022-03-01' = {
name: '${applicationName}-web-${resourceToken}'
location: location
identity: {
type: 'SystemAssigned'
}
properties: {
serverFarmId: appServicePlan.id
siteConfig: {
appSettings: [
{
name: 'DATABASE_CONNECTION_STRING'
value: '@Microsoft.KeyVault(VaultName=${keyVault.name};SecretName=database-connection-string)'
}
{
name: 'AZURE_CLIENT_ID'
value: webApp.identity.principalId
}
]
}
}
}
๐ Rangkaian dan Kesalinghubungan
Konfigurasi Rangkaian Maya
resource vnet 'Microsoft.Network/virtualNetworks@2023-04-01' = {
name: '${applicationName}-vnet-${resourceToken}'
location: location
tags: tags
properties: {
addressSpace: {
addressPrefixes: ['10.0.0.0/16']
}
subnets: [
{
name: 'app-subnet'
properties: {
addressPrefix: '10.0.1.0/24'
serviceEndpoints: [
{
service: 'Microsoft.Storage'
}
{
service: 'Microsoft.KeyVault'
}
]
}
}
{
name: 'db-subnet'
properties: {
addressPrefix: '10.0.2.0/24'
delegations: [
{
name: 'postgres-delegation'
properties: {
serviceName: 'Microsoft.DBforPostgreSQL/flexibleServers'
}
}
]
}
}
]
}
}
// Private DNS Zone for PostgreSQL
resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
name: '${applicationName}.postgres.database.azure.com'
location: 'global'
tags: tags
}
resource privateDnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
parent: privateDnsZone
name: '${applicationName}-dns-link'
location: 'global'
properties: {
registrationEnabled: false
virtualNetwork: {
id: vnet.id
}
}
}
Gateway Aplikasi dengan SSL
resource publicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = {
name: '${applicationName}-agw-pip-${resourceToken}'
location: location
tags: tags
sku: {
name: 'Standard'
tier: 'Regional'
}
properties: {
publicIPAllocationMethod: 'Static'
}
}
resource applicationGateway 'Microsoft.Network/applicationGateways@2023-04-01' = {
name: '${applicationName}-agw-${resourceToken}'
location: location
tags: tags
properties: {
sku: {
name: 'Standard_v2'
tier: 'Standard_v2'
capacity: 1
}
gatewayIPConfigurations: [
{
name: 'appGatewayIpConfig'
properties: {
subnet: {
id: '${vnet.id}/subnets/gateway-subnet'
}
}
}
]
frontendIPConfigurations: [
{
name: 'appGatewayFrontendIP'
properties: {
publicIPAddress: {
id: publicIP.id
}
}
}
]
frontendPorts: [
{
name: 'port80'
properties: {
port: 80
}
}
{
name: 'port443'
properties: {
port: 443
}
}
]
}
}
๐ Pemantauan dan Kebolehlihatan
Application Insights
resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
name: '${applicationName}-logs-${resourceToken}'
location: location
tags: tags
properties: {
sku: {
name: 'PerGB2018'
}
retentionInDays: 30
}
}
resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = {
name: '${applicationName}-ai-${resourceToken}'
location: location
tags: tags
kind: 'web'
properties: {
Application_Type: 'web'
WorkspaceResourceId: logAnalytics.id
}
}
// Output connection string for applications
output APPLICATION_INSIGHTS_CONNECTION_STRING string = applicationInsights.properties.ConnectionString
Metrik dan Amaran Tersuai
resource cpuAlert 'Microsoft.Insights/metricAlerts@2018-03-01' = {
name: '${applicationName}-cpu-alert'
location: 'global'
tags: tags
properties: {
description: 'Alert when CPU usage is high'
severity: 2
enabled: true
scopes: [webApp.id]
evaluationFrequency: 'PT5M'
windowSize: 'PT5M'
criteria: {
'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria'
allOf: [
{
name: 'CPU Usage'
metricName: 'CpuPercentage'
operator: 'GreaterThan'
threshold: 80
timeAggregation: 'Average'
}
]
}
actions: [
{
actionGroupId: actionGroup.id
}
]
}
}
๐ง Konfigurasi Khusus Persekitaran
Fail Parameter untuk Persekitaran Berbeza
// infra/main.parameters.dev.json
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"environmentName": {
"value": "${AZURE_ENV_NAME}"
},
"location": {
"value": "${AZURE_LOCATION}"
},
"appServiceSku": {
"value": "B1"
},
"databaseSku": {
"value": "Standard_B1ms"
},
"enableBackup": {
"value": false
}
}
}
// infra/main.parameters.prod.json
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"environmentName": {
"value": "${AZURE_ENV_NAME}"
},
"location": {
"value": "${AZURE_LOCATION}"
},
"appServiceSku": {
"value": "P1v3"
},
"databaseSku": {
"value": "Standard_D2s_v3"
},
"enableBackup": {
"value": true
},
"replicaCount": {
"value": 3
}
}
}
Penyediaan Sumber Bersyarat
@description('Environment type (dev, staging, prod)')
@allowed(['dev', 'staging', 'prod'])
param environmentType string = 'dev'
// Development resources
resource devStorage 'Microsoft.Storage/storageAccounts@2023-01-01' = if (environmentType == 'dev') {
name: '${applicationName}devstorage${resourceToken}'
location: location
kind: 'StorageV2'
sku: {
name: 'Standard_LRS'
}
}
// Production resources with geo-redundancy
resource prodStorage 'Microsoft.Storage/storageAccounts@2023-01-01' = if (environmentType == 'prod') {
name: '${applicationName}prodstorage${resourceToken}'
location: location
kind: 'StorageV2'
sku: {
name: 'Standard_GRS'
}
properties: {
accessTier: 'Hot'
supportsHttpsTrafficOnly: true
minimumTlsVersion: 'TLS1_2'
}
}
๐ Corak Penyediaan Maju
Penyebaran Pelbagai Wilayah
@description('Primary region')
param primaryLocation string = 'eastus2'
@description('Secondary region')
param secondaryLocation string = 'westus2'
// Primary region resources
module primaryRegion 'modules/region.bicep' = {
name: 'primary-region'
params: {
location: primaryLocation
isPrimary: true
applicationName: applicationName
environmentName: environmentName
}
}
// Secondary region resources
module secondaryRegion 'modules/region.bicep' = {
name: 'secondary-region'
params: {
location: secondaryLocation
isPrimary: false
applicationName: applicationName
environmentName: environmentName
}
}
// Traffic Manager for global load balancing
resource trafficManager 'Microsoft.Network/trafficmanagerprofiles@2022-04-01' = {
name: '${applicationName}-tm-${resourceToken}'
location: 'global'
properties: {
profileStatus: 'Enabled'
trafficRoutingMethod: 'Priority'
dnsConfig: {
relativeName: '${applicationName}-${environmentName}'
ttl: 30
}
monitorConfig: {
protocol: 'HTTPS'
port: 443
path: '/health'
}
endpoints: [
{
name: 'primary-endpoint'
type: 'Microsoft.Network/trafficManagerProfiles/azureEndpoints'
properties: {
targetResourceId: primaryRegion.outputs.webAppId
priority: 1
}
}
{
name: 'secondary-endpoint'
type: 'Microsoft.Network/trafficManagerProfiles/azureEndpoints'
properties: {
targetResourceId: secondaryRegion.outputs.webAppId
priority: 2
}
}
]
}
}
Ujian Infrastruktur
// infra/test/main.test.bicep
param location string = resourceGroup().location
module mainTemplate '../main.bicep' = {
name: 'main-template-test'
params: {
environmentName: 'test'
location: location
applicationName: 'testapp'
}
}
// Test assertions
resource testScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = {
name: 'test-deployment'
location: location
kind: 'AzurePowerShell'
properties: {
azPowerShellVersion: '8.0'
scriptContent: '''
$webAppName = "${mainTemplate.outputs.WEB_NAME}"
$response = Invoke-WebRequest -Uri "https://${mainTemplate.outputs.WEB_URL}/health" -UseBasicParsing
if ($response.StatusCode -ne 200) {
throw "Health check failed"
}
Write-Output "All tests passed!"
'''
timeout: 'PT10M'
cleanupPreference: 'OnSuccess'
retentionInterval: 'P1D'
}
}
๐งช Pratonton & Pengesahan Infrastruktur (BARU)
Pratonton Perubahan Infrastruktur Sebelum Penyebaran
Ciri azd provision --preview membolehkan anda mensimulasikan penyediaan infrastruktur sebelum benar-benar menyebarkan sumber. Ia serupa dengan terraform plan atau bicep what-if, memberikan anda tinjauan larian kering tentang perubahan yang akan dibuat ke persekitaran Azure anda.
๐ ๏ธ Apa Yang Dilakukan
- Menganalisis templat IaC anda (Bicep atau Terraform)
- Menunjukkan pratonton perubahan sumber: penambahan, penghapusan, kemas kini
- Tidak melaksanakan perubahan โ cuma baca dan selamat untuk dijalankan
Kes Penggunaan
# Pratonton perubahan infrastruktur sebelum penyebaran
azd provision --preview
# Pratonton untuk persekitaran tertentu
azd provision --preview -e production
Perintah ini membantu anda:
- Mengesahkan perubahan infrastruktur sebelum mengesahkan sumber
- Mengesan kesilapan konfigurasi lebih awal dalam kitaran pembangunan
- Bekerjasama dengan selamat dalam persekitaran pasukan
- Memastikan penyebaran keistimewaan minimum tanpa kejutan
Ia sangat berguna apabila:
- Bekerja dengan persekitaran pelbagai perkhidmatan yang kompleks
- Membuat perubahan pada infrastruktur produksi
- Mengesah pengubahsuaian templat sebelum kelulusan PR
- Melatih ahli pasukan baru mengenai corak infrastruktur
Contoh Output Pratonton
Output pratonton tepat berbeza mengikut penyedia dan struktur projek, tetapi hasilnya sepatutnya jelas menunjukkan perubahan yang dicadangkan sebelum apa-apa dilaksanakan.
$ azd provision --preview
๐ Previewing infrastructure changes...
The following resources will be created:
+ azurerm_resource_group.rg
+ azurerm_app_service_plan.plan
+ azurerm_linux_web_app.web
+ azurerm_cosmosdb_account.cosmos
The following resources will be modified:
~ azurerm_key_vault.kv
~ access_policy (forces replacement)
The following resources will be destroyed:
- azurerm_storage_account.old_storage
โ ๏ธ Warning: 1 resource will be replaced
โ
Preview completed successfully!
๏ฟฝ๐ Kemas Kini dan Migrasi Sumber
Kemas Kini Sumber Selamat
# Pratonton perubahan infrastruktur dahulu (DISYORKAN)
azd provision --preview
# Terapkan perubahan selepas pengesahan pratonton
azd provision --confirm-with-no-prompt
# Untuk rollback, gunakan Git untuk membalikkan perubahan infrastruktur:
git revert HEAD # Balikkan komit infrastruktur terakhir
azd provision # Terapkan keadaan infrastruktur sebelumnya
Migrasi Pangkalan Data
resource migrationScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = {
name: 'database-migration'
location: location
kind: 'AzureCLI'
properties: {
azCliVersion: '2.40.0'
scriptContent: '''
# Install database migration tools
npm install -g db-migrate db-migrate-pg
# Run migrations
db-migrate up --config database.json --env production
echo "Database migration completed successfully"
'''
environmentVariables: [
{
name: 'DATABASE_URL'
secureValue: databaseConnectionString
}
]
timeout: 'PT30M'
cleanupPreference: 'OnSuccess'
}
}
๐ฏ Amalan Terbaik
1. Konvensyen Penamaan Sumber
var naming = {
resourceGroup: 'rg-${applicationName}-${environmentName}-${location}'
appService: '${applicationName}-web-${resourceToken}'
database: '${applicationName}-db-${resourceToken}'
storage: '${take(replace(applicationName, '-', ''), 15)}${environmentName}sa${take(resourceToken, 8)}'
keyVault: '${take(applicationName, 15)}-kv-${take(resourceToken, 8)}'
}
2. Strategi Penandaan
var commonTags = {
'azd-env-name': environmentName
'azd-app': applicationName
'environment': environmentName
'cost-center': 'engineering'
'owner': 'platform-team'
'project': applicationName
'created-date': utcNow('yyyy-MM-dd')
}
3. Pengesahan Parameter
@description('Environment name')
@minLength(3)
@maxLength(20)
param environmentName string
@description('Location for resources')
@allowed(['eastus2', 'westus2', 'centralus'])
param location string
@description('App Service SKU')
@allowed(['B1', 'B2', 'S1', 'S2', 'P1v3', 'P2v3'])
param appServiceSku string = 'B1'
4. Pengorganisasian Output
// Service endpoints
output WEB_URL string = 'https://${webApp.properties.defaultHostName}'
output API_URL string = 'https://${apiApp.properties.defaultHostName}'
// Resource identifiers
output WEB_APP_NAME string = webApp.name
output API_APP_NAME string = apiApp.name
output DATABASE_NAME string = database.name
// Connection strings (for secure reference)
output DATABASE_CONNECTION_STRING_KEY string = '@Microsoft.KeyVault(VaultName=${keyVault.name};SecretName=database-connection-string)'
Langkah Seterusnya
- Perancangan Pra-penyebaran - Mengesahkan ketersediaan sumber
- Isu Biasa - Menyelesaikan masalah infrastruktur
- Panduan Debugging - Menjejak isu penyediaan
- Pemilihan SKU - Memilih tahap perkhidmatan yang sesuai
Sumber Tambahan
- Dokumentasi Azure Bicep
- Templat Pengurus Sumber Azure
- Pusat Seni Bina Azure
- Kerangka Kerja Azure Yang Baik
Navigasi
- Pelajaran Sebelumnya: Panduan Penyebaran
- Pelajaran Seterusnya: Perancangan Kapasiti
Penafian:
Dokumen ini telah diterjemahkan menggunakan perkhidmatan terjemahan AI Co-op Translator. Walaupun kami berusaha untuk ketepatan, sila ambil maklum bahawa terjemahan automatik mungkin mengandungi kesilapan atau ketidaktepatan. Dokumen asal dalam bahasa asalnya harus dianggap sebagai sumber yang sah. Untuk maklumat kritikal, terjemahan profesional oleh manusia adalah disyorkan. Kami tidak bertanggungjawab terhadap sebarang salah faham atau salah tafsir yang timbul daripada penggunaan terjemahan ini.