Microsoft.AzureStackHCI.Management.psm1
# # AzureStack HCI Registration and Unregistration Powershell Cmdlets. # $ErrorActionPreference = 'Stop' $UsageServiceFirstPartyAppId = "1322e676-dee7-41ee-a874-ac923822781c" function Setup-Logging{ param( [string] $LogFilePrefix ) $date = Get-Date $datestring = "{0}{1:d2}{2:d2}-{3:d2}{4:d2}" -f $date.year,$date.month,$date.day,$date.hour,$date.minute $LogFileName = $LogFilePrefix + "_" + $datestring + ".log" Start-Transcript -LiteralPath $LogFileName -Append | out-null } function Get-ResourceId{ param( [string] $ResourceName, [string] $Subscription, [string] $ResourceGroupName ) return "/subscriptions/" + $Subscription + "/resourceGroups/" + $ResourceGroupName + "/providers/Microsoft.AzureStackHCI/clusters/" + $ResourceName } function Check-UsageAppRoles{ param( [string] $AppId ) Write-Host "Checking admin consent status for AAD Application" $AppId $usagesp = Get-AzureADServicePrincipal -Filter "AppId eq '$UsageServiceFirstPartyAppId'" $usageWritePermission = $usagesp.AppRoles| Where-Object {$_.Value -contains "Cluster.Usage.Write"} $metadataWritePermission = $usagesp.AppRoles| Where-Object {$_.Value -contains "Cluster.Metadata.Write"} $appSP = Get-AzureADServicePrincipal -Filter "AppId eq '$AppId'" $assignedPerms = Get-AzureADServiceAppRoleAssignedTo -ObjectId $appSP.ObjectId $usageWrite = $assignedPerms | where { ($_.Id -eq $usageWritePermission.Id) } $metadataWrite = $assignedPerms | where { ($_.Id -eq $metadataWritePermission.Id) } if($usageWrite -ne $Null -and $metadataWrite -ne $Null) # Check both Usage.Write and Metadata.Write are in consented state. { if($usageWrite.DeletionTimestamp -eq $Null -or ($usageWrite.DeletionTimestamp -lt $usageWrite.CreationTimestamp)) { if($metadataWrite.DeletionTimestamp -eq $Null -or ($metadataWrite.DeletionTimestamp -lt $metadataWrite.CreationTimestamp)) { return $True } } } return $false } function Create-Application{ param( [string] $AppName ) Write-Host "Creating AAD Application" $AppName $usagesp = Get-AzureADServicePrincipal -Filter "AppId eq '$UsageServiceFirstPartyAppId'" $usageWritePermission = $usagesp.AppRoles| Where-Object {$_.Value -contains "Cluster.Usage.Write"} $metadataWritePermission = $usagesp.AppRoles| Where-Object {$_.Value -contains "Cluster.Metadata.Write"} $requiredResourcesAccess = New-Object System.Collections.Generic.List[Microsoft.Open.AzureAD.Model.RequiredResourceAccess] $requiredAccess = New-Object Microsoft.Open.AzureAD.Model.RequiredResourceAccess $requiredAccess.ResourceAppId = $usagesp.AppId $requiredAccess.ResourceAccess = New-Object System.Collections.Generic.List[Microsoft.Open.AzureAD.Model.ResourceAccess] $usageWriteAccess = New-Object Microsoft.Open.AzureAD.Model.ResourceAccess $usageWriteAccess.Type = "Role" $usageWriteAccess.Id = $usageWritePermission.Id $requiredAccess.ResourceAccess.Add($usageWriteAccess) $metadataWriteAccess = New-Object Microsoft.Open.AzureAD.Model.ResourceAccess $metadataWriteAccess.Type = "Role" $metadataWriteAccess.Id = $metadataWritePermission.Id $requiredAccess.ResourceAccess.Add($metadataWriteAccess) $requiredResourcesAccess.Add($requiredAccess) # Create application $app = New-AzureADApplication -DisplayName $AppName -RequiredResourceAccess $requiredResourcesAccess $sp = New-AzureADServicePrincipal -AppId $app.AppId Write-Host "Created new AAD Application" $app.AppId return $app.AppId } function Grant-AdminConsent{ param( [string] $AppId ) Write-Host "Granting admin consent for AAD Application Id" $AppId $usagesp = Get-AzureADServicePrincipal -SearchString "Microsoft.AzureStackHCI Usage Service" $usageWritePermission = $usagesp.AppRoles| Where-Object {$_.Value -contains "Cluster.Usage.Write"} $metadataWritePermission = $usagesp.AppRoles| Where-Object {$_.Value -contains "Cluster.Metadata.Write"} $appSP = Get-AzureADServicePrincipal -Filter "AppId eq '$AppId'" try { New-AzureADServiceAppRoleAssignment -ObjectId $appSP.ObjectId -PrincipalId $appSP.ObjectId -ResourceId $usagesp.ObjectId -Id $metadataWritePermission.Id New-AzureADServiceAppRoleAssignment -ObjectId $appSP.ObjectId -PrincipalId $appSP.ObjectId -ResourceId $usagesp.ObjectId -Id $usageWritePermission.Id } catch { Write-Host "Exception occured when granting admin consent" $ErrorMessage = $_.Exception.Message Write-Host $ErrorMessage return $False } return $True } function Azure-Login{ param( [string] $Subscription, [string] $TenantId, [string] $ArmAccessToken, [string] $GraphAccessToken, [string] $AccountId, [bool] $IsDogfood ) Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force Install-Module -Name Az.Accounts -Force -AllowClobber Install-Module -Name Az.Resources -Force -AllowClobber Install-Module -Name AzureAD -Force -AllowClobber if($IsDogfood) { $azEnv = Add-AzEnvironment -Name Dogfood -PublishSettingsFileUrl "https://windows.azure-test.net/publishsettings/index" -ServiceEndpoint "https://management-preview.core.windows-int.net/" -ManagementPortalUrl "https://windows.azure-test.net/" -ActiveDirectoryEndpoint "https://login.windows-ppe.net/" -ActiveDirectoryServiceEndpointResourceId "https://management.core.windows.net/" -ResourceManagerEndpoint "https://api-dogfood.resources.windows-int.net/" -GalleryEndpoint "https://df.gallery.azure-test.net/" -GraphEndpoint "https://graph.ppe.windows.net/" -GraphAudience "https://graph.ppe.windows.net/" if([string]::IsNullOrEmpty($ArmAccessToken) -or [string]::IsNullOrEmpty($GraphAccessToken) -or [string]::IsNullOrEmpty($AccountId)) { $azProfile = Connect-AzAccount -Environment Dogfood -TenantId $TenantId -Subscription $Subscription -Scope Process $azAD = Connect-AzureAD -AzureEnvironmentName AzurePPE -TenantId $TenantId } else { $azProfile = Connect-AzAccount -Environment Dogfood -TenantId $TenantId -Subscription $Subscription -AccessToken $ArmAccessToken -AccountId $AccountId -Scope Process $azAD = Connect-AzureAD -AzureEnvironmentName AzurePPE -TenantId $TenantId -AadAccessToken $GraphAccessToken -AccountId $AccountId } } else { if([string]::IsNullOrEmpty($ArmAccessToken) -or [string]::IsNullOrEmpty($GraphAccessToken) -or [string]::IsNullOrEmpty($AccountId)) { $azProfile = Connect-AzAccount -TenantId $TenantId -Subscription $Subscription -Scope Process $azAD = Connect-AzureAD -TenantId $TenantId } else { $azProfile = Connect-AzAccount -TenantId $TenantId -Subscription $Subscription -AccessToken $ArmAccessToken -AccountId $AccountId -Scope Process $azAD = Connect-AzureAD -TenantId $TenantId -AadAccessToken $GraphAccessToken -AccountId $AccountId } } } enum OperationStatus { Failed; Success; PendingForAdminConsent; Cancelled } function Register-AzureStackHCI{ param( [string] $Location, [string] $ResourceName, [string] $Subscription, [string] $TenantId, [string] $ResourceGroupName, [string] $ArmAccessToken, [string] $GraphAccessToken, [string] $AccountId, [bool] $IsDogfood ) try { Setup-Logging -LogFilePrefix "RegisterHCI" Write-Host "Register-AzureStackHCI triggered -" "Location:" $Location "ResourceName:" $ResourceName ` "Subscription:" $Subscription "Tenant:" $TenantId "ResourceGroupName:" $ResourceGroupName "AccountId:" $AccountId "IsDogfood:" $IsDogfood Write-Progress -activity "Registering StackHCI with Azure" -status "0% Complete:" -percentcomplete 0 Azure-Login -Subscription $Subscription -TenantId $TenantId -ArmAccessToken $ArmAccessToken -GraphAccessToken $GraphAccessToken -AccountId $AccountId -IsDogfood $IsDogfood Write-Progress -activity "Registering StackHCI with Azure" -status "5% Complete:" -percentcomplete 5 $regRP = Register-AzResourceProvider -ProviderNamespace Microsoft.AzureStackHCI Write-Progress -activity "Registering StackHCI with Azure" -status "10% Complete:" -percentcomplete 10 $resourceId = Get-ResourceId -ResourceName $ResourceName -Subscription $Subscription -ResourceGroupName $ResourceGroupName $resource = Get-AzResource -ResourceId $resourceId -ErrorAction Ignore if($resource -eq $Null) { # Create new application $appId = Create-Application -AppName $ResourceName Write-Host "Created AAD Application with Id" $appId Write-Progress -activity "Registering StackHCI with Azure" -status "25% Complete:" -percentcomplete 25 # Create new resource by calling RP $resGroup = Get-AzResourceGroup -Name $ResourceGroupName -Location $Location -ErrorAction Ignore if($resGroup -eq $Null) { $resGroup = New-AzResourceGroup -Name $ResourceGroupName -Location $Location } $properties = @{"aadClientId"="$appId";"aadTenantId"="$TenantId"} $resource = New-AzResource -ResourceId $resourceId -Location $Location -ApiVersion 2020-03-01-preview -PropertyObject $properties -Force Write-Progress -activity "Registering StackHCI with Azure" -status "40% Complete:" -percentcomplete 40 # Try Granting admin consent for requested permissions $adminConsented = Grant-AdminConsent -AppId $appId if($adminConsented -eq $False) { return [OperationStatus]::PendingForAdminConsent } Write-Progress -activity "Registering StackHCI with Azure" -status "55% Complete:" -percentcomplete 55 } else { # Resource and Application exists. Check admin consent status $appId = $resource.Properties.aadClientId $rolesPresent = Check-UsageAppRoles -AppId $appId Write-Progress -activity "Registering StackHCI with Azure" -status "35% Complete:" -percentcomplete 35 if($rolesPresent -eq $False) { # Try Granting admin consent for requested permissions $adminConsented = Grant-AdminConsent -AppId $appId if($adminConsented -eq $False) { return [OperationStatus]::PendingForAdminConsent } } Write-Progress -activity "Registering StackHCI with Azure" -status "55% Complete:" -percentcomplete 55 } # At this point Application should be created and consented by admin. $appId = $resource.Properties.aadClientId $cloudId = $resource.Properties.cloudId $app = Get-AzureADApplication -Filter "AppId eq '$appId'" $objectId = $app.ObjectId $appSP = Get-AzureADServicePrincipal -Filter "AppId eq '$appId'" $spObjectId = $appSP.ObjectId $RegContext = Get-HCIUsageRegistration if($RegContext.IsRegistered -eq $False) { # Add certificate $certBase64= New-HCIUsageCertificate $Cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]([System.Convert]::FromBase64String($CertBase64)) Write-Progress -activity "Registering StackHCI with Azure" -status "70% Complete:" -percentcomplete 70 $now = [System.DateTime]::UtcNow $appCredential = New-AzureADApplicationKeyCredential -ObjectId $objectId -Type AsymmetricX509Cert -Usage Verify -Value $CertBase64 -StartDate $now -EndDate $Cert.GetExpirationDateString() Write-Progress -activity "Registering StackHCI with Azure" -status "80% Complete:" -percentcomplete 80 # Register by calling on-prem usage service Cmdlet $authority = "https://login.microsoftonline.com" $usageServiceApiScope = "https://azurestackhci-usage.azurewebsites.net/.default" $graphServiceApiScope = "https://graph.microsoft.com/.default" if($IsDogfood) { $authority = "https://login.windows-ppe.net" $usageServiceApiScope = "https://azurestackhci-usage-df.azurewebsites.net/.default" $graphServiceApiScope = "https://graph.ppe.windows.net/.default" } Register-HCIUsage -ServiceEndpoint "https://azurestackhci-usage-df.azurewebsites.net" ` -UsageServiceApiScope $usageServiceApiScope ` -GraphServiceApiScope $graphServiceApiScope ` -AADAuthority $authority ` -AppId $appId ` -TenantId $TenantId ` -CloudId $cloudId ` -SubscriptionId $Subscription ` -Certificate $certBase64 ` -ObjectId $objectId ` -ResourceName $ResourceName ` -ProviderNamespace "Microsoft.AzureStackHCI" ` -ResourceArmId $resourceId ` -ServicePrincipalClientId $spObjectId } Write-Progress -activity "Completed Registering with Azure" -Completed return [OperationStatus]::Success } finally { Stop-Transcript | out-null } } function Unregister-AzureStackHCI{ param( [string] $ResourceName, [string] $Subscription, [string] $TenantId, [string] $ResourceGroupName, [string] $ArmAccessToken, [string] $GraphAccessToken, [string] $AccountId, [bool] $IsDogfood, [bool] $Force ) try { Setup-Logging -LogFilePrefix "UnregisterHCI" Write-Host "Unregister-AzureStackHCI triggered -" "ResourceName:" $ResourceName ` "Subscription:" $Subscription "Tenant:" $TenantId "ResourceGroupName:" $ResourceGroupName ` "AccountId:" $AccountId "IsDogfood:" $IsDogfood "Force:" $Force Write-Progress -activity "Unregistering StackHCI from Azure" -status "0% Complete:" -percentcomplete 0 $confirmation = '' if($Force) { $confirmation = 'y' } else { $confirmation = Read-Host "Are you sure you want to proceed with Unregistration: Type y to proceed" } Write-Progress -activity "Unregistering StackHCI from Azure" -status "5% Complete:" -percentcomplete 5 if ($confirmation -eq 'y') { $RegContext = Get-HCIUsageRegistration $resourceId = Get-ResourceId -ResourceName $ResourceName -Subscription $Subscription -ResourceGroupName $ResourceGroupName Write-Progress -activity "Unregistering StackHCI from Azure" -status "10% Complete:" -percentcomplete 10 Azure-Login -Subscription $Subscription -TenantId $TenantId -ArmAccessToken $ArmAccessToken -GraphAccessToken $GraphAccessToken -AccountId $AccountId -IsDogfood $IsDogfood Write-Progress -activity "Unregistering StackHCI from Azure" -status "30% Complete:" -percentcomplete 30 if($RegContext.IsRegistered -eq $True) { Unregister-HCIUsage } Write-Progress -activity "Unregistering StackHCI from Azure" -status "50% Complete:" -percentcomplete 50 $resource = Get-AzResource -ResourceId $resourceId -ErrorAction Ignore if($resource -ne $Null) { $appId = $resource.Properties.aadClientId $app = Get-AzureADApplication -Filter "AppId eq '$appId'" if($app -ne $Null) { Remove-AzureADApplication -ObjectId $app.ObjectId } Write-Progress -activity "Unregistering StackHCI from Azure" -status "70% Complete:" -percentcomplete 70 $remResource = Remove-AzResource -ResourceId $resourceId -Force } Write-Progress -activity "Unregistering StackHCI from Azure" -status "95% Complete:" -percentcomplete 95 } else { return [OperationStatus]::Cancelled } Write-Progress -activity "Completed Unregistering with Azure" -Completed return [OperationStatus]::Success } finally { Stop-Transcript | out-null } } Export-ModuleMember -Function Register-AzureStackHCI Export-ModuleMember -Function Unregister-AzureStackHCI |