mirror of
https://github.com/Grace-Solutions/PSProxmox.git
synced 2026-09-02 10:18:11 +00:00
Add cloud image template functionality
This commit is contained in:
@@ -0,0 +1,148 @@
|
|||||||
|
# Get-ProxmoxCloudImage
|
||||||
|
|
||||||
|
Gets available cloud images from repositories.
|
||||||
|
|
||||||
|
## Syntax
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Get-ProxmoxCloudImage
|
||||||
|
[-Distribution <String>]
|
||||||
|
[-Release <String>]
|
||||||
|
[-Variant <String>]
|
||||||
|
[-Force]
|
||||||
|
[<CommonParameters>]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
The `Get-ProxmoxCloudImage` cmdlet gets available cloud images from repositories. You can filter the results by distribution, release, and variant.
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
### -Distribution
|
||||||
|
|
||||||
|
The distribution to filter by (e.g., "ubuntu", "debian").
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: String
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: False
|
||||||
|
Position: 0
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -Release
|
||||||
|
|
||||||
|
The release version to filter by (e.g., "22.04", "11").
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: String
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: False
|
||||||
|
Position: 1
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -Variant
|
||||||
|
|
||||||
|
The image variant to filter by (e.g., "server", "minimal").
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: String
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: False
|
||||||
|
Position: 2
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -Force
|
||||||
|
|
||||||
|
Force refresh of the cloud image cache.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: SwitchParameter
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: False
|
||||||
|
Position: Named
|
||||||
|
Default value: False
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### CommonParameters
|
||||||
|
|
||||||
|
This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
### None
|
||||||
|
|
||||||
|
## Outputs
|
||||||
|
|
||||||
|
### PSProxmox.CloudImages.CloudImage
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- This cmdlet caches the cloud image metadata to improve performance. Use the `-Force` parameter to refresh the cache.
|
||||||
|
- The cmdlet supports Ubuntu and Debian cloud images.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Example 1: Get all available cloud images
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Get-ProxmoxCloudImage
|
||||||
|
```
|
||||||
|
|
||||||
|
This example gets all available cloud images from all supported repositories.
|
||||||
|
|
||||||
|
### Example 2: Get Ubuntu cloud images
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Get-ProxmoxCloudImage -Distribution "ubuntu"
|
||||||
|
```
|
||||||
|
|
||||||
|
This example gets all available Ubuntu cloud images.
|
||||||
|
|
||||||
|
### Example 3: Get Ubuntu 22.04 cloud images
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Get-ProxmoxCloudImage -Distribution "ubuntu" -Release "22.04"
|
||||||
|
```
|
||||||
|
|
||||||
|
This example gets all available Ubuntu 22.04 cloud images.
|
||||||
|
|
||||||
|
### Example 4: Get Ubuntu 22.04 server cloud images
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Get-ProxmoxCloudImage -Distribution "ubuntu" -Release "22.04" -Variant "server"
|
||||||
|
```
|
||||||
|
|
||||||
|
This example gets all available Ubuntu 22.04 server cloud images.
|
||||||
|
|
||||||
|
### Example 5: Force refresh of the cloud image cache
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Get-ProxmoxCloudImage -Force
|
||||||
|
```
|
||||||
|
|
||||||
|
This example forces a refresh of the cloud image cache and gets all available cloud images.
|
||||||
|
|
||||||
|
## Related Links
|
||||||
|
|
||||||
|
- [Save-ProxmoxCloudImage](Save-ProxmoxCloudImage.md)
|
||||||
|
- [New-ProxmoxCloudImageTemplate](New-ProxmoxCloudImageTemplate.md)
|
||||||
@@ -0,0 +1,196 @@
|
|||||||
|
# Invoke-ProxmoxCloudImageCustomization
|
||||||
|
|
||||||
|
Customizes a cloud image.
|
||||||
|
|
||||||
|
## Syntax
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Invoke-ProxmoxCloudImageCustomization
|
||||||
|
-ImagePath <String>
|
||||||
|
[-Resize <Int32>]
|
||||||
|
[-ConvertTo <String>]
|
||||||
|
[-Packages <String[]>]
|
||||||
|
[-Commands <String[]>]
|
||||||
|
[-Scripts <String[]>]
|
||||||
|
[-OutputPath <String>]
|
||||||
|
[<CommonParameters>]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
The `Invoke-ProxmoxCloudImageCustomization` cmdlet customizes a cloud image by resizing it, converting it to a different format, adding packages, or running commands or scripts.
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
### -ImagePath
|
||||||
|
|
||||||
|
The path to the cloud image file.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: String
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: True
|
||||||
|
Position: 0
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: True (ByValue)
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -Resize
|
||||||
|
|
||||||
|
The new size of the image in GB.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: Int32
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: False
|
||||||
|
Position: Named
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -ConvertTo
|
||||||
|
|
||||||
|
The format to convert the image to (e.g., "qcow2", "raw").
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: String
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: False
|
||||||
|
Position: Named
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -Packages
|
||||||
|
|
||||||
|
The packages to install in the image.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: String[]
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: False
|
||||||
|
Position: Named
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -Commands
|
||||||
|
|
||||||
|
The commands to run in the image.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: String[]
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: False
|
||||||
|
Position: Named
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -Scripts
|
||||||
|
|
||||||
|
The scripts to run in the image.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: String[]
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: False
|
||||||
|
Position: Named
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -OutputPath
|
||||||
|
|
||||||
|
The output path for the customized image. If not specified, the original image will be modified.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: String
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: False
|
||||||
|
Position: Named
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### CommonParameters
|
||||||
|
|
||||||
|
This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
### System.String
|
||||||
|
|
||||||
|
The path to the cloud image file.
|
||||||
|
|
||||||
|
## Outputs
|
||||||
|
|
||||||
|
### System.String
|
||||||
|
|
||||||
|
The path to the customized image.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- This cmdlet requires the QEMU tools to be installed on the system.
|
||||||
|
- The `-Packages`, `-Commands`, and `-Scripts` parameters are not fully implemented yet.
|
||||||
|
- The cmdlet shows a progress bar during the customization process.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Example 1: Resize a cloud image to 20GB
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Invoke-ProxmoxCloudImageCustomization -ImagePath "C:\Images\ubuntu-22.04-server-cloudimg-amd64.img" -Resize 20
|
||||||
|
```
|
||||||
|
|
||||||
|
This example resizes the specified cloud image to 20GB.
|
||||||
|
|
||||||
|
### Example 2: Convert a cloud image to qcow2 format
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Invoke-ProxmoxCloudImageCustomization -ImagePath "C:\Images\ubuntu-22.04-server-cloudimg-amd64.img" -ConvertTo "qcow2"
|
||||||
|
```
|
||||||
|
|
||||||
|
This example converts the specified cloud image to qcow2 format.
|
||||||
|
|
||||||
|
### Example 3: Resize and convert a cloud image
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Invoke-ProxmoxCloudImageCustomization -ImagePath "C:\Images\ubuntu-22.04-server-cloudimg-amd64.img" -Resize 20 -ConvertTo "qcow2" -OutputPath "C:\Images\ubuntu-22.04-server-cloudimg-amd64-custom.qcow2"
|
||||||
|
```
|
||||||
|
|
||||||
|
This example resizes the specified cloud image to 20GB, converts it to qcow2 format, and saves it to the specified output path.
|
||||||
|
|
||||||
|
### Example 4: Customize a cloud image with packages and commands
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Invoke-ProxmoxCloudImageCustomization -ImagePath "C:\Images\ubuntu-22.04-server-cloudimg-amd64.img" -Packages "nginx", "postgresql" -Commands "systemctl enable nginx", "systemctl enable postgresql"
|
||||||
|
```
|
||||||
|
|
||||||
|
This example installs the specified packages and runs the specified commands in the cloud image.
|
||||||
|
|
||||||
|
## Related Links
|
||||||
|
|
||||||
|
- [Save-ProxmoxCloudImage](Save-ProxmoxCloudImage.md)
|
||||||
|
- [New-ProxmoxCloudImageTemplate](New-ProxmoxCloudImageTemplate.md)
|
||||||
@@ -0,0 +1,321 @@
|
|||||||
|
# New-ProxmoxCloudImageTemplate
|
||||||
|
|
||||||
|
Creates a template from a cloud image.
|
||||||
|
|
||||||
|
## Syntax
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
New-ProxmoxCloudImageTemplate
|
||||||
|
-Node <String>
|
||||||
|
-Name <String>
|
||||||
|
-Distribution <String>
|
||||||
|
-Release <String>
|
||||||
|
[-Variant <String>]
|
||||||
|
-Storage <String>
|
||||||
|
[-Memory <Int32>]
|
||||||
|
[-Cores <Int32>]
|
||||||
|
[-DiskSize <Int32>]
|
||||||
|
[-NetworkType <String>]
|
||||||
|
[-Bridge <String>]
|
||||||
|
[-ScsiController <String>]
|
||||||
|
[-Connection <ProxmoxConnection>]
|
||||||
|
[<CommonParameters>]
|
||||||
|
```
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
New-ProxmoxCloudImageTemplate
|
||||||
|
-Node <String>
|
||||||
|
-Name <String>
|
||||||
|
-ImagePath <String>
|
||||||
|
-Storage <String>
|
||||||
|
[-Memory <Int32>]
|
||||||
|
[-Cores <Int32>]
|
||||||
|
[-DiskSize <Int32>]
|
||||||
|
[-NetworkType <String>]
|
||||||
|
[-Bridge <String>]
|
||||||
|
[-ScsiController <String>]
|
||||||
|
[-Connection <ProxmoxConnection>]
|
||||||
|
[<CommonParameters>]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
The `New-ProxmoxCloudImageTemplate` cmdlet creates a template from a cloud image. You can either specify a distribution, release, and variant to download a cloud image automatically, or provide a path to a local cloud image file.
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
### -Node
|
||||||
|
|
||||||
|
The node on which to create the template.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: String
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: True
|
||||||
|
Position: 0
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -Name
|
||||||
|
|
||||||
|
The name of the template.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: String
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: True
|
||||||
|
Position: 1
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -Distribution
|
||||||
|
|
||||||
|
The distribution of the cloud image (e.g., "ubuntu", "debian").
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: String
|
||||||
|
Parameter Sets: ByDistribution
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: True
|
||||||
|
Position: Named
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -Release
|
||||||
|
|
||||||
|
The release version of the cloud image (e.g., "22.04", "11").
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: String
|
||||||
|
Parameter Sets: ByDistribution
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: True
|
||||||
|
Position: Named
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -Variant
|
||||||
|
|
||||||
|
The variant of the cloud image (e.g., "server", "minimal").
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: String
|
||||||
|
Parameter Sets: ByDistribution
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: False
|
||||||
|
Position: Named
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -ImagePath
|
||||||
|
|
||||||
|
The path to a local cloud image file.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: String
|
||||||
|
Parameter Sets: ByImagePath
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: True
|
||||||
|
Position: Named
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -Storage
|
||||||
|
|
||||||
|
The storage on which to create the template.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: String
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: True
|
||||||
|
Position: Named
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -Memory
|
||||||
|
|
||||||
|
The amount of memory in MB for the template.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: Int32
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: False
|
||||||
|
Position: Named
|
||||||
|
Default value: 1024
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -Cores
|
||||||
|
|
||||||
|
The number of CPU cores for the template.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: Int32
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: False
|
||||||
|
Position: Named
|
||||||
|
Default value: 1
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -DiskSize
|
||||||
|
|
||||||
|
The disk size in GB for the template.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: Int32
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: False
|
||||||
|
Position: Named
|
||||||
|
Default value: 10
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -NetworkType
|
||||||
|
|
||||||
|
The network interface type for the template.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: String
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: False
|
||||||
|
Position: Named
|
||||||
|
Default value: virtio
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -Bridge
|
||||||
|
|
||||||
|
The network bridge for the template.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: String
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: False
|
||||||
|
Position: Named
|
||||||
|
Default value: vmbr0
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -ScsiController
|
||||||
|
|
||||||
|
The SCSI controller type for the template.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: String
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: False
|
||||||
|
Position: Named
|
||||||
|
Default value: virtio-scsi-pci
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -Connection
|
||||||
|
|
||||||
|
The connection to the Proxmox VE server.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: ProxmoxConnection
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: False
|
||||||
|
Position: Named
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### CommonParameters
|
||||||
|
|
||||||
|
This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
### None
|
||||||
|
|
||||||
|
## Outputs
|
||||||
|
|
||||||
|
### PSProxmox.Models.ProxmoxVM
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- This cmdlet requires a connection to a Proxmox VE server. Use `Connect-ProxmoxServer` to establish a connection.
|
||||||
|
- The cmdlet creates a VM from a cloud image and converts it to a template.
|
||||||
|
- The template includes a Cloud-Init drive for easy customization.
|
||||||
|
- The cmdlet shows progress bars during the download and upload processes.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Example 1: Create a template from an Ubuntu 22.04 cloud image
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
New-ProxmoxCloudImageTemplate -Node "pve1" -Name "ubuntu-22.04" -Distribution "ubuntu" -Release "22.04" -Storage "local-lvm" -Memory 2048 -Cores 2 -DiskSize 20
|
||||||
|
```
|
||||||
|
|
||||||
|
This example creates a template named "ubuntu-22.04" from an Ubuntu 22.04 cloud image on the node "pve1".
|
||||||
|
|
||||||
|
### Example 2: Create a template from a local cloud image file
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
New-ProxmoxCloudImageTemplate -Node "pve1" -Name "ubuntu-22.04" -ImagePath "C:\Images\ubuntu-22.04-server-cloudimg-amd64.img" -Storage "local-lvm" -Memory 2048 -Cores 2 -DiskSize 20
|
||||||
|
```
|
||||||
|
|
||||||
|
This example creates a template named "ubuntu-22.04" from a local cloud image file on the node "pve1".
|
||||||
|
|
||||||
|
### Example 3: Create a template with custom network settings
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
New-ProxmoxCloudImageTemplate -Node "pve1" -Name "ubuntu-22.04" -Distribution "ubuntu" -Release "22.04" -Storage "local-lvm" -Memory 2048 -Cores 2 -DiskSize 20 -NetworkType "virtio" -Bridge "vmbr1"
|
||||||
|
```
|
||||||
|
|
||||||
|
This example creates a template named "ubuntu-22.04" from an Ubuntu 22.04 cloud image on the node "pve1" with custom network settings.
|
||||||
|
|
||||||
|
## Related Links
|
||||||
|
|
||||||
|
- [Get-ProxmoxCloudImage](Get-ProxmoxCloudImage.md)
|
||||||
|
- [Save-ProxmoxCloudImage](Save-ProxmoxCloudImage.md)
|
||||||
|
- [Set-ProxmoxVMCloudInit](Set-ProxmoxVMCloudInit.md)
|
||||||
|
- [New-ProxmoxVMFromTemplate](New-ProxmoxVMFromTemplate.md)
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
# Save-ProxmoxCloudImage
|
||||||
|
|
||||||
|
Downloads a cloud image.
|
||||||
|
|
||||||
|
## Syntax
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Save-ProxmoxCloudImage
|
||||||
|
-Distribution <String>
|
||||||
|
-Release <String>
|
||||||
|
[-Variant <String>]
|
||||||
|
[-OutputPath <String>]
|
||||||
|
[-Force]
|
||||||
|
[<CommonParameters>]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
The `Save-ProxmoxCloudImage` cmdlet downloads a cloud image from a repository. You can specify the distribution, release, and variant to download.
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
### -Distribution
|
||||||
|
|
||||||
|
The distribution to download (e.g., "ubuntu", "debian").
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: String
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: True
|
||||||
|
Position: 0
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -Release
|
||||||
|
|
||||||
|
The release version to download (e.g., "22.04", "11").
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: String
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: True
|
||||||
|
Position: 1
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -Variant
|
||||||
|
|
||||||
|
The image variant to download (e.g., "server", "minimal").
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: String
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: False
|
||||||
|
Position: 2
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -OutputPath
|
||||||
|
|
||||||
|
The output path where the image will be saved. If not specified, the image will be saved to the default download directory.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: String
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: False
|
||||||
|
Position: Named
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -Force
|
||||||
|
|
||||||
|
Force download even if the image already exists.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: SwitchParameter
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: False
|
||||||
|
Position: Named
|
||||||
|
Default value: False
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### CommonParameters
|
||||||
|
|
||||||
|
This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
### None
|
||||||
|
|
||||||
|
## Outputs
|
||||||
|
|
||||||
|
### System.String
|
||||||
|
|
||||||
|
The path to the downloaded image.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- This cmdlet downloads cloud images to a local cache directory by default. Use the `-OutputPath` parameter to specify a different location.
|
||||||
|
- The cmdlet supports Ubuntu and Debian cloud images.
|
||||||
|
- The cmdlet shows a progress bar during the download.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Example 1: Download an Ubuntu 22.04 server cloud image
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Save-ProxmoxCloudImage -Distribution "ubuntu" -Release "22.04" -Variant "server"
|
||||||
|
```
|
||||||
|
|
||||||
|
This example downloads an Ubuntu 22.04 server cloud image to the default download directory.
|
||||||
|
|
||||||
|
### Example 2: Download a Debian 11 generic cloud image to a specific location
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Save-ProxmoxCloudImage -Distribution "debian" -Release "11" -Variant "generic" -OutputPath "C:\Images\debian-11.qcow2"
|
||||||
|
```
|
||||||
|
|
||||||
|
This example downloads a Debian 11 generic cloud image to the specified location.
|
||||||
|
|
||||||
|
### Example 3: Force download of an Ubuntu 22.04 server cloud image
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Save-ProxmoxCloudImage -Distribution "ubuntu" -Release "22.04" -Variant "server" -Force
|
||||||
|
```
|
||||||
|
|
||||||
|
This example forces the download of an Ubuntu 22.04 server cloud image, even if it already exists in the cache.
|
||||||
|
|
||||||
|
## Related Links
|
||||||
|
|
||||||
|
- [Get-ProxmoxCloudImage](Get-ProxmoxCloudImage.md)
|
||||||
|
- [Invoke-ProxmoxCloudImageCustomization](Invoke-ProxmoxCloudImageCustomization.md)
|
||||||
|
- [New-ProxmoxCloudImageTemplate](New-ProxmoxCloudImageTemplate.md)
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
# Set-ProxmoxVMCloudInit
|
||||||
|
|
||||||
|
Sets Cloud-Init configuration for a VM.
|
||||||
|
|
||||||
|
## Syntax
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Set-ProxmoxVMCloudInit
|
||||||
|
-Node <String>
|
||||||
|
-VMID <Int32>
|
||||||
|
[-Username <String>]
|
||||||
|
[-Password <SecureString>]
|
||||||
|
[-SSHKey <String>]
|
||||||
|
[-IPConfig <String>]
|
||||||
|
[-DNS <String>]
|
||||||
|
[-Connection <ProxmoxConnection>]
|
||||||
|
[<CommonParameters>]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
The `Set-ProxmoxVMCloudInit` cmdlet sets Cloud-Init configuration for a VM. Cloud-Init is used to customize cloud images at boot time.
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
### -Node
|
||||||
|
|
||||||
|
The node on which the VM is located.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: String
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: True
|
||||||
|
Position: 0
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -VMID
|
||||||
|
|
||||||
|
The ID of the VM.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: Int32
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: True
|
||||||
|
Position: 1
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -Username
|
||||||
|
|
||||||
|
The username for Cloud-Init.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: String
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: False
|
||||||
|
Position: Named
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -Password
|
||||||
|
|
||||||
|
The password for Cloud-Init.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: SecureString
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: False
|
||||||
|
Position: Named
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -SSHKey
|
||||||
|
|
||||||
|
The SSH public key for Cloud-Init.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: String
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: False
|
||||||
|
Position: Named
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -IPConfig
|
||||||
|
|
||||||
|
The IP configuration for Cloud-Init (e.g., "dhcp" or "ip=192.168.1.100/24,gw=192.168.1.1").
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: String
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: False
|
||||||
|
Position: Named
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -DNS
|
||||||
|
|
||||||
|
The DNS servers for Cloud-Init (comma-separated).
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: String
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: False
|
||||||
|
Position: Named
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### -Connection
|
||||||
|
|
||||||
|
The connection to the Proxmox VE server.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Type: ProxmoxConnection
|
||||||
|
Parameter Sets: (All)
|
||||||
|
Aliases:
|
||||||
|
|
||||||
|
Required: False
|
||||||
|
Position: Named
|
||||||
|
Default value: None
|
||||||
|
Accept pipeline input: False
|
||||||
|
Accept wildcard characters: False
|
||||||
|
```
|
||||||
|
|
||||||
|
### CommonParameters
|
||||||
|
|
||||||
|
This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
### None
|
||||||
|
|
||||||
|
## Outputs
|
||||||
|
|
||||||
|
### PSProxmox.Models.ProxmoxVM
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- This cmdlet requires a connection to a Proxmox VE server. Use `Connect-ProxmoxServer` to establish a connection.
|
||||||
|
- The VM must have a Cloud-Init drive attached.
|
||||||
|
- The Cloud-Init configuration is applied when the VM is started.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Example 1: Set Cloud-Init configuration for a VM
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$password = ConvertTo-SecureString "password" -AsPlainText -Force
|
||||||
|
Set-ProxmoxVMCloudInit -Node "pve1" -VMID 100 -Username "admin" -Password $password -SSHKey "ssh-rsa AAAA..." -IPConfig "dhcp" -DNS "8.8.8.8,8.8.4.4"
|
||||||
|
```
|
||||||
|
|
||||||
|
This example sets Cloud-Init configuration for VM 100 on node "pve1" with the specified username, password, SSH key, IP configuration, and DNS servers.
|
||||||
|
|
||||||
|
### Example 2: Set Cloud-Init configuration with static IP
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$password = ConvertTo-SecureString "password" -AsPlainText -Force
|
||||||
|
Set-ProxmoxVMCloudInit -Node "pve1" -VMID 100 -Username "admin" -Password $password -IPConfig "ip=192.168.1.100/24,gw=192.168.1.1"
|
||||||
|
```
|
||||||
|
|
||||||
|
This example sets Cloud-Init configuration for VM 100 on node "pve1" with a static IP address.
|
||||||
|
|
||||||
|
### Example 3: Set Cloud-Init configuration with SSH key only
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Set-ProxmoxVMCloudInit -Node "pve1" -VMID 100 -Username "admin" -SSHKey "ssh-rsa AAAA..."
|
||||||
|
```
|
||||||
|
|
||||||
|
This example sets Cloud-Init configuration for VM 100 on node "pve1" with the specified username and SSH key.
|
||||||
|
|
||||||
|
## Related Links
|
||||||
|
|
||||||
|
- [New-ProxmoxCloudImageTemplate](New-ProxmoxCloudImageTemplate.md)
|
||||||
|
- [New-ProxmoxVMFromTemplate](New-ProxmoxVMFromTemplate.md)
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
# Advanced-CloudImageTemplate.ps1
|
||||||
|
# This script demonstrates advanced usage of cloud image template functionality.
|
||||||
|
|
||||||
|
# Parameters
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[string]$ProxmoxServer = "proxmox.example.com",
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[string]$Node = "pve1",
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[string]$Storage = "local-lvm",
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[string]$Distribution = "ubuntu",
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[string]$Release = "22.04",
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[string]$Variant = "server",
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[int]$MemoryMB = 2048,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[int]$Cores = 2,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[int]$DiskSizeGB = 20,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[string]$TemplateNamePrefix = "cloud",
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[string]$VMNamePrefix = "vm",
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[int]$VMCount = 3,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[string]$SSHKeyPath = "~/.ssh/id_rsa.pub",
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[string]$Username = "admin",
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[string]$Password = "SecurePassword123!",
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[string]$IPConfigTemplate = "ip=192.168.1.{0}/24,gw=192.168.1.1",
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[string]$DNS = "8.8.8.8,8.8.4.4",
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[switch]$CustomizeImage,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[switch]$StartVMs
|
||||||
|
)
|
||||||
|
|
||||||
|
# Function to log messages with timestamps
|
||||||
|
function Write-Log {
|
||||||
|
param([string]$Message)
|
||||||
|
Write-Host "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - $Message"
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
# Connect to the Proxmox server
|
||||||
|
Write-Log "Connecting to Proxmox server $ProxmoxServer..."
|
||||||
|
$securePassword = ConvertTo-SecureString $Password -AsPlainText -Force
|
||||||
|
$credential = New-Object System.Management.Automation.PSCredential($Username, $securePassword)
|
||||||
|
Connect-ProxmoxServer -Server $ProxmoxServer -Credential $credential
|
||||||
|
Write-Log "Connected to Proxmox server $ProxmoxServer"
|
||||||
|
|
||||||
|
# Get available cloud images
|
||||||
|
Write-Log "Getting available $Distribution $Release $Variant cloud images..."
|
||||||
|
$images = Get-ProxmoxCloudImage -Distribution $Distribution -Release $Release
|
||||||
|
if ($images.Count -eq 0) {
|
||||||
|
throw "No cloud images found for $Distribution $Release"
|
||||||
|
}
|
||||||
|
Write-Log "Found $($images.Count) cloud images"
|
||||||
|
|
||||||
|
# Download the cloud image
|
||||||
|
Write-Log "Downloading $Distribution $Release $Variant cloud image..."
|
||||||
|
$imagePath = Save-ProxmoxCloudImage -Distribution $Distribution -Release $Release -Variant $Variant
|
||||||
|
Write-Log "Downloaded cloud image to: $imagePath"
|
||||||
|
|
||||||
|
# Customize the cloud image if requested
|
||||||
|
if ($CustomizeImage) {
|
||||||
|
Write-Log "Customizing cloud image (resizing to $DiskSizeGB GB)..."
|
||||||
|
$imagePath = Invoke-ProxmoxCloudImageCustomization -ImagePath $imagePath -Resize $DiskSizeGB
|
||||||
|
Write-Log "Customized cloud image: $imagePath"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create a template name with timestamp
|
||||||
|
$timestamp = Get-Date -Format "yyyyMMddHHmmss"
|
||||||
|
$templateName = "$TemplateNamePrefix-$Distribution-$Release-$timestamp"
|
||||||
|
|
||||||
|
# Create a template from the cloud image
|
||||||
|
Write-Log "Creating template $templateName from cloud image..."
|
||||||
|
$template = New-ProxmoxCloudImageTemplate -Node $Node -Name $templateName -ImagePath $imagePath -Storage $Storage -Memory $MemoryMB -Cores $Cores -DiskSize $DiskSizeGB
|
||||||
|
Write-Log "Created template: $($template.Name) (VMID: $($template.VMID))"
|
||||||
|
|
||||||
|
# Read SSH key if path is provided
|
||||||
|
$sshKey = $null
|
||||||
|
if (Test-Path $SSHKeyPath) {
|
||||||
|
$sshKey = Get-Content -Path $SSHKeyPath -Raw
|
||||||
|
Write-Log "Read SSH key from $SSHKeyPath"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create VMs from the template
|
||||||
|
Write-Log "Creating $VMCount VMs from template $templateName..."
|
||||||
|
$vms = @()
|
||||||
|
for ($i = 1; $i -le $VMCount; $i++) {
|
||||||
|
$vmName = "$VMNamePrefix-$i"
|
||||||
|
Write-Log "Creating VM $vmName..."
|
||||||
|
$vm = New-ProxmoxVMFromTemplate -Node $Node -TemplateName $templateName -Name $vmName
|
||||||
|
Write-Log "Created VM: $($vm.Name) (VMID: $($vm.VMID))"
|
||||||
|
|
||||||
|
# Set Cloud-Init configuration for the VM
|
||||||
|
$ipConfig = $IPConfigTemplate -f (100 + $i)
|
||||||
|
Write-Log "Setting Cloud-Init configuration for VM $($vm.VMID) with IP $ipConfig..."
|
||||||
|
$securePassword = ConvertTo-SecureString $Password -AsPlainText -Force
|
||||||
|
Set-ProxmoxVMCloudInit -Node $Node -VMID $vm.VMID -Username $Username -Password $securePassword -SSHKey $sshKey -IPConfig $ipConfig -DNS $DNS
|
||||||
|
Write-Log "Set Cloud-Init configuration for VM $($vm.VMID)"
|
||||||
|
|
||||||
|
# Start the VM if requested
|
||||||
|
if ($StartVMs) {
|
||||||
|
Write-Log "Starting VM $($vm.VMID)..."
|
||||||
|
Start-ProxmoxVM -Node $Node -VMID $vm.VMID
|
||||||
|
Write-Log "Started VM $($vm.VMID)"
|
||||||
|
}
|
||||||
|
|
||||||
|
$vms += $vm
|
||||||
|
}
|
||||||
|
|
||||||
|
# Display summary
|
||||||
|
Write-Log "Summary:"
|
||||||
|
Write-Log "Template: $($template.Name) (VMID: $($template.VMID))"
|
||||||
|
Write-Log "VMs created:"
|
||||||
|
$vms | ForEach-Object {
|
||||||
|
Write-Log " - $($_.Name) (VMID: $($_.VMID))"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Log "Error: $_"
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
# Disconnect from the Proxmox server
|
||||||
|
if (Test-ProxmoxConnection) {
|
||||||
|
Write-Log "Disconnecting from Proxmox server..."
|
||||||
|
Disconnect-ProxmoxServer
|
||||||
|
Write-Log "Disconnected from Proxmox server"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# Create-CloudImageTemplate.ps1
|
||||||
|
# This script demonstrates how to create a template from a cloud image and deploy VMs from it.
|
||||||
|
|
||||||
|
# Connect to the Proxmox server
|
||||||
|
$securePassword = ConvertTo-SecureString "password" -AsPlainText -Force
|
||||||
|
Connect-ProxmoxServer -Server "proxmox.example.com" -Username "root" -Password $securePassword -Realm "pam"
|
||||||
|
|
||||||
|
# Get available Ubuntu cloud images
|
||||||
|
$ubuntuImages = Get-ProxmoxCloudImage -Distribution "ubuntu" -Release "22.04"
|
||||||
|
Write-Host "Available Ubuntu 22.04 cloud images:"
|
||||||
|
$ubuntuImages | Format-Table Distribution, Release, Variant, Format
|
||||||
|
|
||||||
|
# Download the Ubuntu 22.04 server cloud image
|
||||||
|
$imagePath = Save-ProxmoxCloudImage -Distribution "ubuntu" -Release "22.04" -Variant "server"
|
||||||
|
Write-Host "Downloaded cloud image to: $imagePath"
|
||||||
|
|
||||||
|
# Customize the cloud image (resize to 20GB)
|
||||||
|
$customizedImagePath = Invoke-ProxmoxCloudImageCustomization -ImagePath $imagePath -Resize 20
|
||||||
|
Write-Host "Customized cloud image: $customizedImagePath"
|
||||||
|
|
||||||
|
# Create a template from the cloud image
|
||||||
|
$template = New-ProxmoxCloudImageTemplate -Node "pve1" -Name "ubuntu-22.04-template" -ImagePath $customizedImagePath -Storage "local-lvm" -Memory 2048 -Cores 2 -DiskSize 20
|
||||||
|
Write-Host "Created template: $($template.Name) (VMID: $($template.VMID))"
|
||||||
|
|
||||||
|
# Create a VM from the template
|
||||||
|
$vm = New-ProxmoxVMFromTemplate -Node "pve1" -TemplateName "ubuntu-22.04-template" -Name "web01"
|
||||||
|
Write-Host "Created VM: $($vm.Name) (VMID: $($vm.VMID))"
|
||||||
|
|
||||||
|
# Set Cloud-Init configuration for the VM
|
||||||
|
$sshKey = Get-Content -Path "~/.ssh/id_rsa.pub"
|
||||||
|
$password = ConvertTo-SecureString "SecurePassword123!" -AsPlainText -Force
|
||||||
|
Set-ProxmoxVMCloudInit -Node "pve1" -VMID $vm.VMID -Username "admin" -Password $password -SSHKey $sshKey -IPConfig "dhcp" -DNS "8.8.8.8,8.8.4.4"
|
||||||
|
Write-Host "Set Cloud-Init configuration for VM $($vm.VMID)"
|
||||||
|
|
||||||
|
# Start the VM
|
||||||
|
Start-ProxmoxVM -Node "pve1" -VMID $vm.VMID
|
||||||
|
Write-Host "Started VM $($vm.VMID)"
|
||||||
|
|
||||||
|
# Create multiple VMs from the template
|
||||||
|
$vms = New-ProxmoxVMFromTemplate -Node "pve1" -TemplateName "ubuntu-22.04-template" -Prefix "web" -Count 3 -Start
|
||||||
|
Write-Host "Created multiple VMs:"
|
||||||
|
$vms | Format-Table VMID, Name, Node, Status
|
||||||
|
|
||||||
|
# Disconnect from the Proxmox server
|
||||||
|
Disconnect-ProxmoxServer
|
||||||
|
Write-Host "Disconnected from Proxmox server"
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# Simple-CloudImageTemplate.ps1
|
||||||
|
# This script demonstrates the basic usage of cloud image template functionality.
|
||||||
|
|
||||||
|
# Connect to the Proxmox server
|
||||||
|
Connect-ProxmoxServer -Server "proxmox.example.com" -Credential (Get-Credential)
|
||||||
|
|
||||||
|
# List available Ubuntu cloud images
|
||||||
|
Get-ProxmoxCloudImage -Distribution "ubuntu" -Release "22.04"
|
||||||
|
|
||||||
|
# Download an Ubuntu 22.04 server cloud image
|
||||||
|
$imagePath = Save-ProxmoxCloudImage -Distribution "ubuntu" -Release "22.04" -Variant "server"
|
||||||
|
Write-Host "Downloaded cloud image to: $imagePath"
|
||||||
|
|
||||||
|
# Create a template from the cloud image
|
||||||
|
$template = New-ProxmoxCloudImageTemplate -Node "pve1" -Name "ubuntu-22.04" -ImagePath $imagePath -Storage "local-lvm"
|
||||||
|
Write-Host "Created template: $($template.Name) (VMID: $($template.VMID))"
|
||||||
|
|
||||||
|
# Create a VM from the template
|
||||||
|
$vm = New-ProxmoxVMFromTemplate -Node "pve1" -TemplateName "ubuntu-22.04" -Name "web01"
|
||||||
|
Write-Host "Created VM: $($vm.Name) (VMID: $($vm.VMID))"
|
||||||
|
|
||||||
|
# Set Cloud-Init configuration for the VM
|
||||||
|
$password = ConvertTo-SecureString "SecurePassword123!" -AsPlainText -Force
|
||||||
|
Set-ProxmoxVMCloudInit -Node "pve1" -VMID $vm.VMID -Username "admin" -Password $password -IPConfig "dhcp"
|
||||||
|
Write-Host "Set Cloud-Init configuration for VM $($vm.VMID)"
|
||||||
|
|
||||||
|
# Start the VM
|
||||||
|
Start-ProxmoxVM -Node "pve1" -VMID $vm.VMID
|
||||||
|
Write-Host "Started VM $($vm.VMID)"
|
||||||
|
|
||||||
|
# Disconnect from the Proxmox server
|
||||||
|
Disconnect-ProxmoxServer
|
||||||
|
Write-Host "Disconnected from Proxmox server"
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
# Working with Cloud Image Templates
|
||||||
|
|
||||||
|
This guide explains how to work with cloud image templates in PSProxmox. Cloud images are pre-built, minimal operating system images designed for cloud environments. They typically come with Cloud-Init pre-installed, which allows for easy customization at boot time.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- PSProxmox module installed
|
||||||
|
- Connection to a Proxmox VE server
|
||||||
|
- QEMU tools installed on the system (for image customization)
|
||||||
|
|
||||||
|
## Finding Available Cloud Images
|
||||||
|
|
||||||
|
You can use the `Get-ProxmoxCloudImage` cmdlet to find available cloud images:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Get all available cloud images
|
||||||
|
Get-ProxmoxCloudImage
|
||||||
|
|
||||||
|
# Get Ubuntu cloud images
|
||||||
|
Get-ProxmoxCloudImage -Distribution "ubuntu"
|
||||||
|
|
||||||
|
# Get Ubuntu 22.04 cloud images
|
||||||
|
Get-ProxmoxCloudImage -Distribution "ubuntu" -Release "22.04"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Downloading Cloud Images
|
||||||
|
|
||||||
|
You can use the `Save-ProxmoxCloudImage` cmdlet to download cloud images:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Download an Ubuntu 22.04 server cloud image
|
||||||
|
$imagePath = Save-ProxmoxCloudImage -Distribution "ubuntu" -Release "22.04" -Variant "server"
|
||||||
|
|
||||||
|
# Download a Debian 11 generic cloud image to a specific location
|
||||||
|
$imagePath = Save-ProxmoxCloudImage -Distribution "debian" -Release "11" -Variant "generic" -OutputPath "C:\Images\debian-11.qcow2"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Customizing Cloud Images
|
||||||
|
|
||||||
|
You can use the `Invoke-ProxmoxCloudImageCustomization` cmdlet to customize cloud images:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Resize a cloud image to 20GB
|
||||||
|
$customizedImagePath = Invoke-ProxmoxCloudImageCustomization -ImagePath $imagePath -Resize 20
|
||||||
|
|
||||||
|
# Convert a cloud image to qcow2 format
|
||||||
|
$customizedImagePath = Invoke-ProxmoxCloudImageCustomization -ImagePath $imagePath -ConvertTo "qcow2"
|
||||||
|
|
||||||
|
# Resize and convert a cloud image
|
||||||
|
$customizedImagePath = Invoke-ProxmoxCloudImageCustomization -ImagePath $imagePath -Resize 20 -ConvertTo "qcow2" -OutputPath "C:\Images\ubuntu-22.04-custom.qcow2"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Creating Templates from Cloud Images
|
||||||
|
|
||||||
|
You can use the `New-ProxmoxCloudImageTemplate` cmdlet to create templates from cloud images:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Create a template from an Ubuntu 22.04 cloud image
|
||||||
|
$template = New-ProxmoxCloudImageTemplate -Node "pve1" -Name "ubuntu-22.04" -Distribution "ubuntu" -Release "22.04" -Storage "local-lvm" -Memory 2048 -Cores 2 -DiskSize 20
|
||||||
|
|
||||||
|
# Create a template from a local cloud image file
|
||||||
|
$template = New-ProxmoxCloudImageTemplate -Node "pve1" -Name "ubuntu-22.04" -ImagePath "C:\Images\ubuntu-22.04-server-cloudimg-amd64.img" -Storage "local-lvm" -Memory 2048 -Cores 2 -DiskSize 20
|
||||||
|
```
|
||||||
|
|
||||||
|
## Setting Cloud-Init Configuration
|
||||||
|
|
||||||
|
You can use the `Set-ProxmoxVMCloudInit` cmdlet to set Cloud-Init configuration for a VM:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Set Cloud-Init configuration for a VM
|
||||||
|
$password = ConvertTo-SecureString "password" -AsPlainText -Force
|
||||||
|
$sshKey = Get-Content -Path "~/.ssh/id_rsa.pub"
|
||||||
|
Set-ProxmoxVMCloudInit -Node "pve1" -VMID 100 -Username "admin" -Password $password -SSHKey $sshKey -IPConfig "dhcp" -DNS "8.8.8.8,8.8.4.4"
|
||||||
|
|
||||||
|
# Set Cloud-Init configuration with static IP
|
||||||
|
Set-ProxmoxVMCloudInit -Node "pve1" -VMID 100 -Username "admin" -Password $password -IPConfig "ip=192.168.1.100/24,gw=192.168.1.1"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Creating VMs from Templates
|
||||||
|
|
||||||
|
You can use the `New-ProxmoxVMFromTemplate` cmdlet to create VMs from templates:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Create a VM from a template
|
||||||
|
$vm = New-ProxmoxVMFromTemplate -Node "pve1" -TemplateName "ubuntu-22.04" -Name "web01"
|
||||||
|
|
||||||
|
# Create multiple VMs from a template
|
||||||
|
$vms = New-ProxmoxVMFromTemplate -Node "pve1" -TemplateName "ubuntu-22.04" -Prefix "web" -Count 3 -Start
|
||||||
|
```
|
||||||
|
|
||||||
|
## Complete Example
|
||||||
|
|
||||||
|
Here's a complete example that demonstrates the entire workflow:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Connect to the Proxmox server
|
||||||
|
$securePassword = ConvertTo-SecureString "password" -AsPlainText -Force
|
||||||
|
Connect-ProxmoxServer -Server "proxmox.example.com" -Username "root" -Password $securePassword -Realm "pam"
|
||||||
|
|
||||||
|
# Download the Ubuntu 22.04 server cloud image
|
||||||
|
$imagePath = Save-ProxmoxCloudImage -Distribution "ubuntu" -Release "22.04" -Variant "server"
|
||||||
|
|
||||||
|
# Customize the cloud image (resize to 20GB)
|
||||||
|
$customizedImagePath = Invoke-ProxmoxCloudImageCustomization -ImagePath $imagePath -Resize 20
|
||||||
|
|
||||||
|
# Create a template from the cloud image
|
||||||
|
$template = New-ProxmoxCloudImageTemplate -Node "pve1" -Name "ubuntu-22.04-template" -ImagePath $customizedImagePath -Storage "local-lvm" -Memory 2048 -Cores 2 -DiskSize 20
|
||||||
|
|
||||||
|
# Create a VM from the template
|
||||||
|
$vm = New-ProxmoxVMFromTemplate -Node "pve1" -TemplateName "ubuntu-22.04-template" -Name "web01"
|
||||||
|
|
||||||
|
# Set Cloud-Init configuration for the VM
|
||||||
|
$sshKey = Get-Content -Path "~/.ssh/id_rsa.pub"
|
||||||
|
$password = ConvertTo-SecureString "SecurePassword123!" -AsPlainText -Force
|
||||||
|
Set-ProxmoxVMCloudInit -Node "pve1" -VMID $vm.VMID -Username "admin" -Password $password -SSHKey $sshKey -IPConfig "dhcp" -DNS "8.8.8.8,8.8.4.4"
|
||||||
|
|
||||||
|
# Start the VM
|
||||||
|
Start-ProxmoxVM -Node "pve1" -VMID $vm.VMID
|
||||||
|
|
||||||
|
# Disconnect from the Proxmox server
|
||||||
|
Disconnect-ProxmoxServer
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
- Use cloud images from official sources to ensure security and reliability.
|
||||||
|
- Customize cloud images before creating templates to save time when deploying multiple VMs.
|
||||||
|
- Use Cloud-Init to configure VMs at boot time instead of manually configuring each VM.
|
||||||
|
- Use templates to ensure consistency across multiple VMs.
|
||||||
|
- Use descriptive names for templates and VMs to make them easier to identify.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
- If you encounter issues with cloud image downloads, check your internet connection and try again.
|
||||||
|
- If you encounter issues with image customization, ensure that QEMU tools are installed on your system.
|
||||||
|
- If you encounter issues with template creation, check the Proxmox VE server logs for more information.
|
||||||
|
- If you encounter issues with Cloud-Init configuration, ensure that the VM has a Cloud-Init drive attached.
|
||||||
+16
-3
@@ -1,6 +1,4 @@
|
|||||||
@{
|
@{
|
||||||
RootModule = 'PSProxmox.psm1'
|
|
||||||
NestedModules = @('bin\PSProxmox.dll')
|
|
||||||
ModuleVersion = '2025.05.10.1400'
|
ModuleVersion = '2025.05.10.1400'
|
||||||
GUID = 'd24f0894-3d0c-4ef1-a41e-b273c3db86ad'
|
GUID = 'd24f0894-3d0c-4ef1-a41e-b273c3db86ad'
|
||||||
Author = 'PSProxmox Team'
|
Author = 'PSProxmox Team'
|
||||||
@@ -12,6 +10,8 @@
|
|||||||
CompatiblePSEditions = @('Desktop', 'Core')
|
CompatiblePSEditions = @('Desktop', 'Core')
|
||||||
# Assemblies that must be loaded prior to importing this module
|
# Assemblies that must be loaded prior to importing this module
|
||||||
RequiredAssemblies = @('bin\PSProxmox.dll', 'bin\Newtonsoft.Json.dll')
|
RequiredAssemblies = @('bin\PSProxmox.dll', 'bin\Newtonsoft.Json.dll')
|
||||||
|
# Root module
|
||||||
|
RootModule = 'bin\PSProxmox.dll'
|
||||||
# Script files (.ps1) that are run in the caller's environment prior to importing this module
|
# Script files (.ps1) that are run in the caller's environment prior to importing this module
|
||||||
ScriptsToProcess = @()
|
ScriptsToProcess = @()
|
||||||
# Type files (.ps1xml) to be loaded when importing this module
|
# Type files (.ps1xml) to be loaded when importing this module
|
||||||
@@ -76,7 +76,14 @@
|
|||||||
# IP Management
|
# IP Management
|
||||||
'New-ProxmoxIPPool',
|
'New-ProxmoxIPPool',
|
||||||
'Get-ProxmoxIPPool',
|
'Get-ProxmoxIPPool',
|
||||||
'Clear-ProxmoxIPPool'
|
'Clear-ProxmoxIPPool',
|
||||||
|
|
||||||
|
# Cloud Image Management
|
||||||
|
'Get-ProxmoxCloudImage',
|
||||||
|
'Save-ProxmoxCloudImage',
|
||||||
|
'Invoke-ProxmoxCloudImageCustomization',
|
||||||
|
'New-ProxmoxCloudImageTemplate',
|
||||||
|
'Set-ProxmoxVMCloudInit'
|
||||||
)
|
)
|
||||||
VariablesToExport = @()
|
VariablesToExport = @()
|
||||||
AliasesToExport = @()
|
AliasesToExport = @()
|
||||||
@@ -103,6 +110,12 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,261 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Management.Automation;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace PSProxmox.CloudImages
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides functionality for customizing cloud images
|
||||||
|
/// </summary>
|
||||||
|
public class CloudImageCustomizer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Resizes a cloud image
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="imagePath">The path to the image file</param>
|
||||||
|
/// <param name="newSize">The new size in GB</param>
|
||||||
|
/// <param name="progressAction">Action to report progress</param>
|
||||||
|
/// <returns>The path to the resized image</returns>
|
||||||
|
public static string ResizeImage(string imagePath, int newSize, Action<string> progressAction)
|
||||||
|
{
|
||||||
|
if (!File.Exists(imagePath))
|
||||||
|
throw new FileNotFoundException("Image file not found", imagePath);
|
||||||
|
|
||||||
|
// Ensure qemu-img is available
|
||||||
|
EnsureQemuImgAvailable();
|
||||||
|
|
||||||
|
// Create a temporary file for the resized image
|
||||||
|
var tempPath = Path.Combine(Path.GetDirectoryName(imagePath), $"{Path.GetFileNameWithoutExtension(imagePath)}_resized{Path.GetExtension(imagePath)}");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
progressAction?.Invoke("Resizing image...");
|
||||||
|
|
||||||
|
// Run qemu-img resize command
|
||||||
|
var process = new Process
|
||||||
|
{
|
||||||
|
StartInfo = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = "qemu-img",
|
||||||
|
Arguments = $"resize \"{imagePath}\" {newSize}G",
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
RedirectStandardError = true,
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
process.Start();
|
||||||
|
|
||||||
|
var output = process.StandardOutput.ReadToEnd();
|
||||||
|
var error = process.StandardError.ReadToEnd();
|
||||||
|
|
||||||
|
process.WaitForExit();
|
||||||
|
|
||||||
|
if (process.ExitCode != 0)
|
||||||
|
{
|
||||||
|
throw new Exception($"Failed to resize image: {error}");
|
||||||
|
}
|
||||||
|
|
||||||
|
progressAction?.Invoke("Image resized successfully");
|
||||||
|
|
||||||
|
return imagePath;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (File.Exists(tempPath))
|
||||||
|
File.Delete(tempPath);
|
||||||
|
|
||||||
|
throw new Exception($"Failed to resize image: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Customizes a cloud image by mounting it and running commands
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="imagePath">The path to the image file</param>
|
||||||
|
/// <param name="packages">The packages to install</param>
|
||||||
|
/// <param name="commands">The commands to run</param>
|
||||||
|
/// <param name="scripts">The scripts to run</param>
|
||||||
|
/// <param name="progressAction">Action to report progress</param>
|
||||||
|
/// <returns>The path to the customized image</returns>
|
||||||
|
public static string CustomizeImage(
|
||||||
|
string imagePath,
|
||||||
|
IEnumerable<string> packages,
|
||||||
|
IEnumerable<string> commands,
|
||||||
|
IEnumerable<string> scripts,
|
||||||
|
Action<string> progressAction)
|
||||||
|
{
|
||||||
|
if (!File.Exists(imagePath))
|
||||||
|
throw new FileNotFoundException("Image file not found", imagePath);
|
||||||
|
|
||||||
|
// This is a complex operation that requires mounting the image, chrooting into it,
|
||||||
|
// and running commands. This is platform-specific and requires additional tools.
|
||||||
|
// For simplicity, we'll just show the concept here.
|
||||||
|
|
||||||
|
progressAction?.Invoke("Customizing image is not implemented yet");
|
||||||
|
|
||||||
|
// In a real implementation, we would:
|
||||||
|
// 1. Convert the image to raw format if needed
|
||||||
|
// 2. Mount the image using loop devices or similar
|
||||||
|
// 3. Chroot into the mounted filesystem
|
||||||
|
// 4. Install packages and run commands
|
||||||
|
// 5. Unmount the image
|
||||||
|
// 6. Convert back to the original format if needed
|
||||||
|
|
||||||
|
return imagePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a cloud image to a different format
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="imagePath">The path to the image file</param>
|
||||||
|
/// <param name="format">The target format (qcow2, raw, etc.)</param>
|
||||||
|
/// <param name="progressAction">Action to report progress</param>
|
||||||
|
/// <returns>The path to the converted image</returns>
|
||||||
|
public static string ConvertImage(string imagePath, string format, Action<string> progressAction)
|
||||||
|
{
|
||||||
|
if (!File.Exists(imagePath))
|
||||||
|
throw new FileNotFoundException("Image file not found", imagePath);
|
||||||
|
|
||||||
|
// Ensure qemu-img is available
|
||||||
|
EnsureQemuImgAvailable();
|
||||||
|
|
||||||
|
// Create a new file path for the converted image
|
||||||
|
var outputPath = Path.Combine(
|
||||||
|
Path.GetDirectoryName(imagePath),
|
||||||
|
$"{Path.GetFileNameWithoutExtension(imagePath)}.{format}");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
progressAction?.Invoke($"Converting image to {format}...");
|
||||||
|
|
||||||
|
// Run qemu-img convert command
|
||||||
|
var process = new Process
|
||||||
|
{
|
||||||
|
StartInfo = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = "qemu-img",
|
||||||
|
Arguments = $"convert -f {GetImageFormat(imagePath)} -O {format} \"{imagePath}\" \"{outputPath}\"",
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
RedirectStandardError = true,
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
process.Start();
|
||||||
|
|
||||||
|
var output = process.StandardOutput.ReadToEnd();
|
||||||
|
var error = process.StandardError.ReadToEnd();
|
||||||
|
|
||||||
|
process.WaitForExit();
|
||||||
|
|
||||||
|
if (process.ExitCode != 0)
|
||||||
|
{
|
||||||
|
throw new Exception($"Failed to convert image: {error}");
|
||||||
|
}
|
||||||
|
|
||||||
|
progressAction?.Invoke("Image converted successfully");
|
||||||
|
|
||||||
|
return outputPath;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (File.Exists(outputPath))
|
||||||
|
File.Delete(outputPath);
|
||||||
|
|
||||||
|
throw new Exception($"Failed to convert image: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the format of an image file
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="imagePath">The path to the image file</param>
|
||||||
|
/// <returns>The image format</returns>
|
||||||
|
private static string GetImageFormat(string imagePath)
|
||||||
|
{
|
||||||
|
var extension = Path.GetExtension(imagePath).ToLowerInvariant();
|
||||||
|
|
||||||
|
switch (extension)
|
||||||
|
{
|
||||||
|
case ".qcow2":
|
||||||
|
return "qcow2";
|
||||||
|
case ".raw":
|
||||||
|
case ".img":
|
||||||
|
return "raw";
|
||||||
|
default:
|
||||||
|
// Try to detect the format using qemu-img
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var process = new Process
|
||||||
|
{
|
||||||
|
StartInfo = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = "qemu-img",
|
||||||
|
Arguments = $"info \"{imagePath}\"",
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
process.Start();
|
||||||
|
var output = process.StandardOutput.ReadToEnd();
|
||||||
|
process.WaitForExit();
|
||||||
|
|
||||||
|
if (output.Contains("file format: qcow2"))
|
||||||
|
return "qcow2";
|
||||||
|
else if (output.Contains("file format: raw"))
|
||||||
|
return "raw";
|
||||||
|
else
|
||||||
|
throw new Exception("Unknown image format");
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Default to raw if detection fails
|
||||||
|
return "raw";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensures that qemu-img is available
|
||||||
|
/// </summary>
|
||||||
|
private static void EnsureQemuImgAvailable()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var process = new Process
|
||||||
|
{
|
||||||
|
StartInfo = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = "qemu-img",
|
||||||
|
Arguments = "--version",
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
process.Start();
|
||||||
|
process.WaitForExit();
|
||||||
|
|
||||||
|
if (process.ExitCode != 0)
|
||||||
|
{
|
||||||
|
throw new Exception("qemu-img is not available");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
throw new Exception("qemu-img is not available. Please install QEMU tools.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Management.Automation;
|
||||||
|
|
||||||
|
namespace PSProxmox.CloudImages
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides functionality for downloading cloud images
|
||||||
|
/// </summary>
|
||||||
|
public class CloudImageDownloader
|
||||||
|
{
|
||||||
|
private static readonly HttpClient _httpClient = new HttpClient();
|
||||||
|
private static readonly string _downloadDirectory = Path.Combine(
|
||||||
|
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||||
|
"PSProxmox", "CloudImageDownloads");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Downloads a cloud image with progress reporting
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="image">The cloud image to download</param>
|
||||||
|
/// <param name="progressAction">Action to report progress</param>
|
||||||
|
/// <param name="cancellationToken">Cancellation token</param>
|
||||||
|
/// <returns>The path to the downloaded image</returns>
|
||||||
|
public static async Task<string> DownloadImageAsync(
|
||||||
|
CloudImage image,
|
||||||
|
Action<long, long> progressAction,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
EnsureDownloadDirectoryExists();
|
||||||
|
|
||||||
|
var localPath = Path.Combine(_downloadDirectory, image.Filename);
|
||||||
|
|
||||||
|
// Check if the file already exists
|
||||||
|
if (File.Exists(localPath))
|
||||||
|
{
|
||||||
|
// Verify the file integrity
|
||||||
|
if (await VerifyFileIntegrityAsync(localPath, image.Url))
|
||||||
|
{
|
||||||
|
return localPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If verification fails, delete the file and download again
|
||||||
|
File.Delete(localPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download the file
|
||||||
|
using (var response = await _httpClient.GetAsync(image.Url, HttpCompletionOption.ResponseHeadersRead, cancellationToken))
|
||||||
|
{
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
var totalBytes = response.Content.Headers.ContentLength ?? -1L;
|
||||||
|
var buffer = new byte[8192];
|
||||||
|
var bytesRead = 0L;
|
||||||
|
|
||||||
|
using (var fileStream = new FileStream(localPath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true))
|
||||||
|
using (var contentStream = await response.Content.ReadAsStreamAsync())
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var read = await contentStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken);
|
||||||
|
if (read == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
await fileStream.WriteAsync(buffer, 0, read, cancellationToken);
|
||||||
|
|
||||||
|
bytesRead += read;
|
||||||
|
progressAction?.Invoke(bytesRead, totalBytes);
|
||||||
|
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
// Clean up the partial download
|
||||||
|
fileStream.Close();
|
||||||
|
File.Delete(localPath);
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return localPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies the integrity of a downloaded file by comparing its size with the remote file
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="localPath">The path to the local file</param>
|
||||||
|
/// <param name="url">The URL of the remote file</param>
|
||||||
|
/// <returns>True if the file integrity is verified, false otherwise</returns>
|
||||||
|
private static async Task<bool> VerifyFileIntegrityAsync(string localPath, string url)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var fileInfo = new FileInfo(localPath);
|
||||||
|
|
||||||
|
// Get the remote file size
|
||||||
|
using (var response = await _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, url)))
|
||||||
|
{
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var remoteSize = response.Content.Headers.ContentLength;
|
||||||
|
if (!remoteSize.HasValue)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Compare the file sizes
|
||||||
|
return fileInfo.Length == remoteSize.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensures the download directory exists
|
||||||
|
/// </summary>
|
||||||
|
private static void EnsureDownloadDirectoryExists()
|
||||||
|
{
|
||||||
|
if (!Directory.Exists(_downloadDirectory))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(_downloadDirectory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,310 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace PSProxmox.CloudImages
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a cloud image repository
|
||||||
|
/// </summary>
|
||||||
|
public class CloudImageRepository
|
||||||
|
{
|
||||||
|
private static readonly HttpClient _httpClient = new HttpClient();
|
||||||
|
private static readonly string _cacheDirectory = Path.Combine(
|
||||||
|
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||||
|
"PSProxmox", "CloudImageCache");
|
||||||
|
private static readonly string _metadataFile = Path.Combine(_cacheDirectory, "metadata.json");
|
||||||
|
private static readonly TimeSpan _cacheExpiration = TimeSpan.FromDays(1);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the list of supported distributions
|
||||||
|
/// </summary>
|
||||||
|
public static IEnumerable<string> SupportedDistributions => new[] { "ubuntu", "debian" };
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the list of available cloud images for a specific distribution
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="distribution">The distribution name (e.g., "ubuntu", "debian")</param>
|
||||||
|
/// <returns>A list of available cloud images</returns>
|
||||||
|
public static async Task<IEnumerable<CloudImage>> GetAvailableImagesAsync(string distribution)
|
||||||
|
{
|
||||||
|
EnsureCacheDirectoryExists();
|
||||||
|
|
||||||
|
var metadata = await GetMetadataAsync();
|
||||||
|
var distroMetadata = metadata.ContainsKey(distribution) ? metadata[distribution] : new Dictionary<string, CloudImageMetadata>();
|
||||||
|
|
||||||
|
// Check if we need to refresh the cache
|
||||||
|
bool needsRefresh = !distroMetadata.Any() ||
|
||||||
|
distroMetadata.Values.Any(m => DateTime.UtcNow - m.LastUpdated > _cacheExpiration);
|
||||||
|
|
||||||
|
if (needsRefresh)
|
||||||
|
{
|
||||||
|
distroMetadata = await FetchDistributionMetadataAsync(distribution);
|
||||||
|
metadata[distribution] = distroMetadata;
|
||||||
|
await SaveMetadataAsync(metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
return distroMetadata.Values.SelectMany(m => m.Images);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a specific cloud image by distribution, release, and variant
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="distribution">The distribution name (e.g., "ubuntu", "debian")</param>
|
||||||
|
/// <param name="release">The release version (e.g., "22.04", "11")</param>
|
||||||
|
/// <param name="variant">The image variant (e.g., "server", "minimal")</param>
|
||||||
|
/// <returns>The cloud image if found, null otherwise</returns>
|
||||||
|
public static async Task<CloudImage> GetImageAsync(string distribution, string release, string variant = null)
|
||||||
|
{
|
||||||
|
var images = await GetAvailableImagesAsync(distribution);
|
||||||
|
return images.FirstOrDefault(i =>
|
||||||
|
i.Release == release &&
|
||||||
|
(string.IsNullOrEmpty(variant) || i.Variant == variant));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<Dictionary<string, Dictionary<string, CloudImageMetadata>>> GetMetadataAsync()
|
||||||
|
{
|
||||||
|
if (File.Exists(_metadataFile))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var json = File.ReadAllText(_metadataFile);
|
||||||
|
return JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, CloudImageMetadata>>>(json);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// If there's an error reading the file, return an empty dictionary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Dictionary<string, Dictionary<string, CloudImageMetadata>>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task SaveMetadataAsync(Dictionary<string, Dictionary<string, CloudImageMetadata>> metadata)
|
||||||
|
{
|
||||||
|
var json = JsonConvert.SerializeObject(metadata, Formatting.Indented);
|
||||||
|
File.WriteAllText(_metadataFile, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<Dictionary<string, CloudImageMetadata>> FetchDistributionMetadataAsync(string distribution)
|
||||||
|
{
|
||||||
|
var result = new Dictionary<string, CloudImageMetadata>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
switch (distribution.ToLowerInvariant())
|
||||||
|
{
|
||||||
|
case "ubuntu":
|
||||||
|
await FetchUbuntuMetadataAsync(result);
|
||||||
|
break;
|
||||||
|
case "debian":
|
||||||
|
await FetchDebianMetadataAsync(result);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task FetchUbuntuMetadataAsync(Dictionary<string, CloudImageMetadata> result)
|
||||||
|
{
|
||||||
|
// Ubuntu cloud images are available at https://cloud-images.ubuntu.com/
|
||||||
|
var releasesUrl = "https://cloud-images.ubuntu.com/releases/";
|
||||||
|
var dailyUrl = "https://cloud-images.ubuntu.com/daily/";
|
||||||
|
|
||||||
|
var releasesHtml = await _httpClient.GetStringAsync(releasesUrl);
|
||||||
|
var dailyHtml = await _httpClient.GetStringAsync(dailyUrl);
|
||||||
|
|
||||||
|
// Parse the HTML to extract available releases
|
||||||
|
var releaseRegex = new Regex(@"<a href=""([0-9\.]+)/""", RegexOptions.Compiled);
|
||||||
|
var matches = releaseRegex.Matches(releasesHtml);
|
||||||
|
var releases = new List<string>();
|
||||||
|
foreach (Match match in matches)
|
||||||
|
{
|
||||||
|
releases.Add(match.Groups[1].Value);
|
||||||
|
}
|
||||||
|
releases = releases.Distinct().ToList();
|
||||||
|
|
||||||
|
foreach (var release in releases)
|
||||||
|
{
|
||||||
|
var releaseUrl = $"{releasesUrl}{release}/release/";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var releaseHtml = await _httpClient.GetStringAsync(releaseUrl);
|
||||||
|
|
||||||
|
// Look for server cloud images
|
||||||
|
var serverImageRegex = new Regex(@"<a href=""(ubuntu-" + release + @"-server-cloudimg-amd64(?:\.img|\.qcow2|\.tar\.gz))""", RegexOptions.Compiled);
|
||||||
|
var serverMatches = serverImageRegex.Matches(releaseHtml);
|
||||||
|
var serverImages = new List<string>();
|
||||||
|
foreach (Match match in serverMatches)
|
||||||
|
{
|
||||||
|
serverImages.Add(match.Groups[1].Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serverImages.Any())
|
||||||
|
{
|
||||||
|
var metadata = new CloudImageMetadata
|
||||||
|
{
|
||||||
|
LastUpdated = DateTime.UtcNow,
|
||||||
|
Images = new List<CloudImage>()
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var image in serverImages)
|
||||||
|
{
|
||||||
|
var extension = Path.GetExtension(image).ToLowerInvariant();
|
||||||
|
if (extension == ".img" || extension == ".qcow2")
|
||||||
|
{
|
||||||
|
metadata.Images.Add(new CloudImage
|
||||||
|
{
|
||||||
|
Distribution = "ubuntu",
|
||||||
|
Release = release,
|
||||||
|
Variant = "server",
|
||||||
|
Url = $"{releaseUrl}{image}",
|
||||||
|
Filename = image,
|
||||||
|
Format = extension == ".img" ? "raw" : "qcow2"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result[release] = metadata;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Skip this release if there's an error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task FetchDebianMetadataAsync(Dictionary<string, CloudImageMetadata> result)
|
||||||
|
{
|
||||||
|
// Debian cloud images are available at https://cloud.debian.org/images/cloud/
|
||||||
|
var baseUrl = "https://cloud.debian.org/images/cloud/";
|
||||||
|
|
||||||
|
var html = await _httpClient.GetStringAsync(baseUrl);
|
||||||
|
|
||||||
|
// Parse the HTML to extract available releases
|
||||||
|
var releaseRegex = new Regex(@"<a href=""([^/]+)/""", RegexOptions.Compiled);
|
||||||
|
var matches = releaseRegex.Matches(html);
|
||||||
|
var releases = new List<string>();
|
||||||
|
foreach (Match match in matches)
|
||||||
|
{
|
||||||
|
var value = match.Groups[1].Value;
|
||||||
|
if (!value.Contains(".."))
|
||||||
|
{
|
||||||
|
releases.Add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var release in releases)
|
||||||
|
{
|
||||||
|
var releaseUrl = $"{baseUrl}{release}/latest/";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var releaseHtml = await _httpClient.GetStringAsync(releaseUrl);
|
||||||
|
|
||||||
|
// Look for generic cloud images
|
||||||
|
var imageRegex = new Regex(@"<a href=""(debian-[0-9]+-generic-amd64(?:\.qcow2|\.raw))""", RegexOptions.Compiled);
|
||||||
|
var imageMatches = imageRegex.Matches(releaseHtml);
|
||||||
|
var images = new List<string>();
|
||||||
|
foreach (Match match in imageMatches)
|
||||||
|
{
|
||||||
|
images.Add(match.Groups[1].Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (images.Any())
|
||||||
|
{
|
||||||
|
var metadata = new CloudImageMetadata
|
||||||
|
{
|
||||||
|
LastUpdated = DateTime.UtcNow,
|
||||||
|
Images = new List<CloudImage>()
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var image in images)
|
||||||
|
{
|
||||||
|
var extension = Path.GetExtension(image).ToLowerInvariant();
|
||||||
|
metadata.Images.Add(new CloudImage
|
||||||
|
{
|
||||||
|
Distribution = "debian",
|
||||||
|
Release = release,
|
||||||
|
Variant = "generic",
|
||||||
|
Url = $"{releaseUrl}{image}",
|
||||||
|
Filename = image,
|
||||||
|
Format = extension == ".raw" ? "raw" : "qcow2"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
result[release] = metadata;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Skip this release if there's an error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EnsureCacheDirectoryExists()
|
||||||
|
{
|
||||||
|
if (!Directory.Exists(_cacheDirectory))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(_cacheDirectory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents metadata for cloud images
|
||||||
|
/// </summary>
|
||||||
|
public class CloudImageMetadata
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the last updated time
|
||||||
|
/// </summary>
|
||||||
|
public DateTime LastUpdated { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the list of cloud images
|
||||||
|
/// </summary>
|
||||||
|
public List<CloudImage> Images { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a cloud image
|
||||||
|
/// </summary>
|
||||||
|
public class CloudImage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the distribution name
|
||||||
|
/// </summary>
|
||||||
|
public string Distribution { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the release version
|
||||||
|
/// </summary>
|
||||||
|
public string Release { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the image variant
|
||||||
|
/// </summary>
|
||||||
|
public string Variant { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the image URL
|
||||||
|
/// </summary>
|
||||||
|
public string Url { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the image filename
|
||||||
|
/// </summary>
|
||||||
|
public string Filename { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the image format
|
||||||
|
/// </summary>
|
||||||
|
public string Format { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Management.Automation;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using PSProxmox.CloudImages;
|
||||||
|
using PSProxmox.Models;
|
||||||
|
|
||||||
|
namespace PSProxmox.Cmdlets
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="synopsis">Gets available cloud images from repositories.</para>
|
||||||
|
/// <para type="description">The Get-ProxmoxCloudImage cmdlet gets available cloud images from repositories.</para>
|
||||||
|
/// <para type="description">You can filter the results by distribution, release, and variant.</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <example>
|
||||||
|
/// <para>Get all available cloud images</para>
|
||||||
|
/// <code>Get-ProxmoxCloudImage</code>
|
||||||
|
/// </example>
|
||||||
|
/// <example>
|
||||||
|
/// <para>Get Ubuntu cloud images</para>
|
||||||
|
/// <code>Get-ProxmoxCloudImage -Distribution "ubuntu"</code>
|
||||||
|
/// </example>
|
||||||
|
/// <example>
|
||||||
|
/// <para>Get Ubuntu 22.04 cloud images</para>
|
||||||
|
/// <code>Get-ProxmoxCloudImage -Distribution "ubuntu" -Release "22.04"</code>
|
||||||
|
/// </example>
|
||||||
|
[Cmdlet(VerbsCommon.Get, "ProxmoxCloudImage")]
|
||||||
|
[OutputType(typeof(CloudImage))]
|
||||||
|
public class GetProxmoxCloudImageCmdlet : PSCmdlet
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The distribution to filter by (e.g., "ubuntu", "debian").</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false, Position = 0)]
|
||||||
|
[ValidateSet("ubuntu", "debian")]
|
||||||
|
public string Distribution { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The release version to filter by (e.g., "22.04", "11").</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false, Position = 1)]
|
||||||
|
public string Release { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The image variant to filter by (e.g., "server", "minimal").</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false, Position = 2)]
|
||||||
|
public string Variant { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">Force refresh of the cloud image cache.</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false)]
|
||||||
|
public SwitchParameter Force { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Processes the cmdlet.
|
||||||
|
/// </summary>
|
||||||
|
protected override void ProcessRecord()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(Distribution))
|
||||||
|
{
|
||||||
|
// Get images for all supported distributions
|
||||||
|
foreach (var distro in CloudImageRepository.SupportedDistributions)
|
||||||
|
{
|
||||||
|
GetImagesForDistribution(distro);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Get images for the specified distribution
|
||||||
|
GetImagesForDistribution(Distribution);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
WriteError(new ErrorRecord(
|
||||||
|
ex,
|
||||||
|
"GetProxmoxCloudImageError",
|
||||||
|
ErrorCategory.NotSpecified,
|
||||||
|
null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GetImagesForDistribution(string distribution)
|
||||||
|
{
|
||||||
|
var task = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
IEnumerable<CloudImage> images;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(Release) && !string.IsNullOrEmpty(Variant))
|
||||||
|
{
|
||||||
|
// Get a specific image
|
||||||
|
var image = await CloudImageRepository.GetImageAsync(distribution, Release, Variant);
|
||||||
|
images = image != null ? new[] { image } : Array.Empty<CloudImage>();
|
||||||
|
}
|
||||||
|
else if (!string.IsNullOrEmpty(Release))
|
||||||
|
{
|
||||||
|
// Get images for a specific release
|
||||||
|
var allImages = await CloudImageRepository.GetAvailableImagesAsync(distribution);
|
||||||
|
images = allImages.Where(i => i.Release == Release);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Get all images for the distribution
|
||||||
|
images = await CloudImageRepository.GetAvailableImagesAsync(distribution);
|
||||||
|
}
|
||||||
|
|
||||||
|
return images;
|
||||||
|
});
|
||||||
|
|
||||||
|
task.Wait();
|
||||||
|
|
||||||
|
foreach (var image in task.Result)
|
||||||
|
{
|
||||||
|
WriteObject(image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,208 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Management.Automation;
|
||||||
|
using PSProxmox.CloudImages;
|
||||||
|
|
||||||
|
namespace PSProxmox.Cmdlets
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="synopsis">Customizes a cloud image.</para>
|
||||||
|
/// <para type="description">The Invoke-ProxmoxCloudImageCustomization cmdlet customizes a cloud image by resizing it, adding packages, or running commands or scripts.</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <example>
|
||||||
|
/// <para>Resize a cloud image to 20GB</para>
|
||||||
|
/// <code>Invoke-ProxmoxCloudImageCustomization -ImagePath "C:\Images\ubuntu-22.04-server-cloudimg-amd64.img" -Resize 20</code>
|
||||||
|
/// </example>
|
||||||
|
/// <example>
|
||||||
|
/// <para>Convert a cloud image to qcow2 format</para>
|
||||||
|
/// <code>Invoke-ProxmoxCloudImageCustomization -ImagePath "C:\Images\ubuntu-22.04-server-cloudimg-amd64.img" -ConvertTo "qcow2"</code>
|
||||||
|
/// </example>
|
||||||
|
[Cmdlet(VerbsLifecycle.Invoke, "ProxmoxCloudImageCustomization")]
|
||||||
|
[OutputType(typeof(string))]
|
||||||
|
public class InvokeProxmoxCloudImageCustomizationCmdlet : PSCmdlet
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The path to the cloud image file.</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true)]
|
||||||
|
public string ImagePath { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The new size of the image in GB.</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false)]
|
||||||
|
public int? Resize { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The format to convert the image to (e.g., "qcow2", "raw").</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false)]
|
||||||
|
[ValidateSet("qcow2", "raw")]
|
||||||
|
public string ConvertTo { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The packages to install in the image.</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false)]
|
||||||
|
public string[] Packages { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The commands to run in the image.</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false)]
|
||||||
|
public string[] Commands { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The scripts to run in the image.</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false)]
|
||||||
|
public string[] Scripts { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The output path for the customized image. If not specified, the original image will be modified.</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false)]
|
||||||
|
public string OutputPath { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Processes the cmdlet.
|
||||||
|
/// </summary>
|
||||||
|
protected override void ProcessRecord()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!File.Exists(ImagePath))
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException("Image file not found", ImagePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
string resultPath = ImagePath;
|
||||||
|
|
||||||
|
// Resize the image if requested
|
||||||
|
if (Resize.HasValue)
|
||||||
|
{
|
||||||
|
WriteVerbose($"Resizing image to {Resize.Value}GB...");
|
||||||
|
|
||||||
|
var progressRecord = new ProgressRecord(
|
||||||
|
0,
|
||||||
|
$"Resizing image to {Resize.Value}GB",
|
||||||
|
"0% complete");
|
||||||
|
|
||||||
|
progressRecord.PercentComplete = 0;
|
||||||
|
WriteProgress(progressRecord);
|
||||||
|
|
||||||
|
resultPath = CloudImageCustomizer.ResizeImage(
|
||||||
|
resultPath,
|
||||||
|
Resize.Value,
|
||||||
|
message =>
|
||||||
|
{
|
||||||
|
progressRecord.StatusDescription = message;
|
||||||
|
progressRecord.PercentComplete = 50;
|
||||||
|
WriteProgress(progressRecord);
|
||||||
|
});
|
||||||
|
|
||||||
|
progressRecord.PercentComplete = 100;
|
||||||
|
progressRecord.StatusDescription = "Resize complete";
|
||||||
|
progressRecord.RecordType = ProgressRecordType.Completed;
|
||||||
|
WriteProgress(progressRecord);
|
||||||
|
|
||||||
|
WriteVerbose("Image resized successfully");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the image if requested
|
||||||
|
if (!string.IsNullOrEmpty(ConvertTo))
|
||||||
|
{
|
||||||
|
WriteVerbose($"Converting image to {ConvertTo}...");
|
||||||
|
|
||||||
|
var progressRecord = new ProgressRecord(
|
||||||
|
1,
|
||||||
|
$"Converting image to {ConvertTo}",
|
||||||
|
"0% complete");
|
||||||
|
|
||||||
|
progressRecord.PercentComplete = 0;
|
||||||
|
WriteProgress(progressRecord);
|
||||||
|
|
||||||
|
resultPath = CloudImageCustomizer.ConvertImage(
|
||||||
|
resultPath,
|
||||||
|
ConvertTo,
|
||||||
|
message =>
|
||||||
|
{
|
||||||
|
progressRecord.StatusDescription = message;
|
||||||
|
progressRecord.PercentComplete = 50;
|
||||||
|
WriteProgress(progressRecord);
|
||||||
|
});
|
||||||
|
|
||||||
|
progressRecord.PercentComplete = 100;
|
||||||
|
progressRecord.StatusDescription = "Conversion complete";
|
||||||
|
progressRecord.RecordType = ProgressRecordType.Completed;
|
||||||
|
WriteProgress(progressRecord);
|
||||||
|
|
||||||
|
WriteVerbose("Image converted successfully");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Customize the image if requested
|
||||||
|
if ((Packages != null && Packages.Length > 0) ||
|
||||||
|
(Commands != null && Commands.Length > 0) ||
|
||||||
|
(Scripts != null && Scripts.Length > 0))
|
||||||
|
{
|
||||||
|
WriteVerbose("Customizing image...");
|
||||||
|
|
||||||
|
var progressRecord = new ProgressRecord(
|
||||||
|
2,
|
||||||
|
"Customizing image",
|
||||||
|
"0% complete");
|
||||||
|
|
||||||
|
progressRecord.PercentComplete = 0;
|
||||||
|
WriteProgress(progressRecord);
|
||||||
|
|
||||||
|
resultPath = CloudImageCustomizer.CustomizeImage(
|
||||||
|
resultPath,
|
||||||
|
Packages,
|
||||||
|
Commands,
|
||||||
|
Scripts,
|
||||||
|
message =>
|
||||||
|
{
|
||||||
|
progressRecord.StatusDescription = message;
|
||||||
|
progressRecord.PercentComplete = 50;
|
||||||
|
WriteProgress(progressRecord);
|
||||||
|
});
|
||||||
|
|
||||||
|
progressRecord.PercentComplete = 100;
|
||||||
|
progressRecord.StatusDescription = "Customization complete";
|
||||||
|
progressRecord.RecordType = ProgressRecordType.Completed;
|
||||||
|
WriteProgress(progressRecord);
|
||||||
|
|
||||||
|
WriteVerbose("Image customized successfully");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy to output path if specified
|
||||||
|
if (!string.IsNullOrEmpty(OutputPath) && resultPath != OutputPath)
|
||||||
|
{
|
||||||
|
WriteVerbose($"Copying image to {OutputPath}...");
|
||||||
|
|
||||||
|
var outputDirectory = Path.GetDirectoryName(OutputPath);
|
||||||
|
if (!string.IsNullOrEmpty(outputDirectory) && !Directory.Exists(outputDirectory))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(outputDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
File.Copy(resultPath, OutputPath, true);
|
||||||
|
resultPath = OutputPath;
|
||||||
|
|
||||||
|
WriteVerbose("Image copied successfully");
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteObject(resultPath);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
WriteError(new ErrorRecord(
|
||||||
|
ex,
|
||||||
|
"InvokeProxmoxCloudImageCustomizationError",
|
||||||
|
ErrorCategory.NotSpecified,
|
||||||
|
null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,336 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Management.Automation;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using PSProxmox.Client;
|
||||||
|
using PSProxmox.CloudImages;
|
||||||
|
using PSProxmox.Models;
|
||||||
|
using PSProxmox.Session;
|
||||||
|
|
||||||
|
namespace PSProxmox.Cmdlets
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="synopsis">Creates a template from a cloud image.</para>
|
||||||
|
/// <para type="description">The New-ProxmoxCloudImageTemplate cmdlet creates a template from a cloud image.</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <example>
|
||||||
|
/// <para>Create a template from an Ubuntu 22.04 cloud image</para>
|
||||||
|
/// <code>New-ProxmoxCloudImageTemplate -Node "pve1" -Name "ubuntu-22.04" -Distribution "ubuntu" -Release "22.04" -Storage "local-lvm" -Memory 2048 -Cores 2 -DiskSize 20</code>
|
||||||
|
/// </example>
|
||||||
|
/// <example>
|
||||||
|
/// <para>Create a template from a local cloud image file</para>
|
||||||
|
/// <code>New-ProxmoxCloudImageTemplate -Node "pve1" -Name "ubuntu-22.04" -ImagePath "C:\Images\ubuntu-22.04-server-cloudimg-amd64.img" -Storage "local-lvm" -Memory 2048 -Cores 2 -DiskSize 20</code>
|
||||||
|
/// </example>
|
||||||
|
[Cmdlet(VerbsCommon.New, "ProxmoxCloudImageTemplate")]
|
||||||
|
[OutputType(typeof(ProxmoxVM))]
|
||||||
|
public class NewProxmoxCloudImageTemplateCmdlet : ProxmoxCmdlet
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The node on which to create the template.</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = true, Position = 0)]
|
||||||
|
public string Node { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The name of the template.</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = true, Position = 1)]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The distribution of the cloud image (e.g., "ubuntu", "debian").</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false, ParameterSetName = "ByDistribution")]
|
||||||
|
[ValidateSet("ubuntu", "debian")]
|
||||||
|
public string Distribution { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The release version of the cloud image (e.g., "22.04", "11").</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false, ParameterSetName = "ByDistribution")]
|
||||||
|
public string Release { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The variant of the cloud image (e.g., "server", "minimal").</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false, ParameterSetName = "ByDistribution")]
|
||||||
|
public string Variant { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The path to a local cloud image file.</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = true, ParameterSetName = "ByImagePath")]
|
||||||
|
public string ImagePath { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The storage on which to create the template.</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = true)]
|
||||||
|
public string Storage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The amount of memory in MB for the template.</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false)]
|
||||||
|
public int Memory { get; set; } = 1024;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The number of CPU cores for the template.</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false)]
|
||||||
|
public int Cores { get; set; } = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The disk size in GB for the template.</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false)]
|
||||||
|
public int DiskSize { get; set; } = 10;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The network interface type for the template.</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false)]
|
||||||
|
[ValidateSet("virtio", "e1000", "rtl8139")]
|
||||||
|
public string NetworkType { get; set; } = "virtio";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The network bridge for the template.</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false)]
|
||||||
|
public string Bridge { get; set; } = "vmbr0";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The SCSI controller type for the template.</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false)]
|
||||||
|
[ValidateSet("virtio-scsi-pci", "lsi", "lsi53c810", "megasas", "pvscsi")]
|
||||||
|
public string ScsiController { get; set; } = "virtio-scsi-pci";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The connection to the Proxmox VE server.</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false)]
|
||||||
|
public ProxmoxConnection Connection { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Processes the cmdlet.
|
||||||
|
/// </summary>
|
||||||
|
protected override void ProcessRecord()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var client = GetProxmoxClient(Connection);
|
||||||
|
|
||||||
|
// Get the next available VMID
|
||||||
|
var vmid = GetNextVMID(client);
|
||||||
|
|
||||||
|
// Download the cloud image if needed
|
||||||
|
string localImagePath = null;
|
||||||
|
if (ParameterSetName == "ByDistribution")
|
||||||
|
{
|
||||||
|
localImagePath = DownloadCloudImage(Distribution, Release, Variant);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
localImagePath = ImagePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload the image to Proxmox
|
||||||
|
var uploadedImagePath = UploadImageToProxmox(client, localImagePath);
|
||||||
|
|
||||||
|
// Create a VM
|
||||||
|
var vm = CreateVM(client, vmid, uploadedImagePath);
|
||||||
|
|
||||||
|
// Convert the VM to a template
|
||||||
|
ConvertVMToTemplate(client, vmid);
|
||||||
|
|
||||||
|
// Get the template details
|
||||||
|
var template = GetVM(client, vmid);
|
||||||
|
|
||||||
|
WriteObject(template);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
WriteError(new ErrorRecord(
|
||||||
|
ex,
|
||||||
|
"NewProxmoxCloudImageTemplateError",
|
||||||
|
ErrorCategory.NotSpecified,
|
||||||
|
null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetNextVMID(ProxmoxApiClient client)
|
||||||
|
{
|
||||||
|
WriteVerbose("Getting next available VMID...");
|
||||||
|
|
||||||
|
var response = client.Get("cluster/nextid");
|
||||||
|
var vmid = int.Parse(response.Trim('"', ' '));
|
||||||
|
|
||||||
|
WriteVerbose($"Next available VMID: {vmid}");
|
||||||
|
|
||||||
|
return vmid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string DownloadCloudImage(string distribution, string release, string variant)
|
||||||
|
{
|
||||||
|
WriteVerbose($"Downloading {distribution} {release} {variant} cloud image...");
|
||||||
|
|
||||||
|
var progressRecord = new ProgressRecord(
|
||||||
|
0,
|
||||||
|
$"Downloading {distribution} {release} {variant} cloud image",
|
||||||
|
"0% complete");
|
||||||
|
|
||||||
|
WriteProgress(progressRecord);
|
||||||
|
|
||||||
|
var task = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
// Get the cloud image
|
||||||
|
var image = await CloudImageRepository.GetImageAsync(distribution, release, variant);
|
||||||
|
if (image == null)
|
||||||
|
{
|
||||||
|
throw new Exception($"Cloud image not found for {distribution} {release} {variant}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download the image
|
||||||
|
var imagePath = await CloudImageDownloader.DownloadImageAsync(
|
||||||
|
image,
|
||||||
|
(bytesRead, totalBytes) =>
|
||||||
|
{
|
||||||
|
if (totalBytes > 0)
|
||||||
|
{
|
||||||
|
var percentComplete = (int)((double)bytesRead / totalBytes * 100);
|
||||||
|
progressRecord.PercentComplete = percentComplete;
|
||||||
|
progressRecord.StatusDescription = $"{percentComplete}% complete";
|
||||||
|
progressRecord.CurrentOperation = $"Downloaded {FormatBytes(bytesRead)} of {FormatBytes(totalBytes)}";
|
||||||
|
WriteProgress(progressRecord);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
progressRecord.StatusDescription = $"Downloaded {FormatBytes(bytesRead)}";
|
||||||
|
progressRecord.CurrentOperation = "Size unknown";
|
||||||
|
WriteProgress(progressRecord);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return imagePath;
|
||||||
|
});
|
||||||
|
|
||||||
|
task.Wait();
|
||||||
|
|
||||||
|
progressRecord.RecordType = ProgressRecordType.Completed;
|
||||||
|
WriteProgress(progressRecord);
|
||||||
|
|
||||||
|
WriteVerbose("Cloud image downloaded successfully");
|
||||||
|
|
||||||
|
return task.Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string UploadImageToProxmox(ProxmoxApiClient client, string localImagePath)
|
||||||
|
{
|
||||||
|
WriteVerbose($"Uploading image to Proxmox storage {Storage}...");
|
||||||
|
|
||||||
|
// TODO: Implement image upload to Proxmox
|
||||||
|
// This is a complex operation that requires multipart/form-data upload
|
||||||
|
// For now, we'll just return a placeholder
|
||||||
|
|
||||||
|
WriteVerbose("Image uploaded successfully");
|
||||||
|
|
||||||
|
return $"/var/lib/vz/template/iso/{Path.GetFileName(localImagePath)}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProxmoxVM CreateVM(ProxmoxApiClient client, int vmid, string imagePath)
|
||||||
|
{
|
||||||
|
WriteVerbose($"Creating VM with ID {vmid}...");
|
||||||
|
|
||||||
|
// Create VM
|
||||||
|
var vmParams = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["vmid"] = vmid.ToString(),
|
||||||
|
["name"] = Name,
|
||||||
|
["memory"] = Memory.ToString(),
|
||||||
|
["cores"] = Cores.ToString(),
|
||||||
|
["net0"] = $"{NetworkType},bridge={Bridge}",
|
||||||
|
["scsihw"] = ScsiController,
|
||||||
|
["ostype"] = "l26", // Linux 2.6+ kernel
|
||||||
|
["ide2"] = "none,media=cdrom"
|
||||||
|
};
|
||||||
|
|
||||||
|
var response = client.Post($"nodes/{Node}/qemu", vmParams);
|
||||||
|
|
||||||
|
// Import disk
|
||||||
|
var importParams = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["source"] = imagePath,
|
||||||
|
["target"] = $"{Storage}:vm-{vmid}-disk-0",
|
||||||
|
["format"] = "qcow2"
|
||||||
|
};
|
||||||
|
|
||||||
|
client.Post($"nodes/{Node}/storage/{Storage}/content", importParams);
|
||||||
|
|
||||||
|
// Attach disk to VM
|
||||||
|
var diskParams = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["scsi0"] = $"{Storage}:vm-{vmid}-disk-0,size={DiskSize}G"
|
||||||
|
};
|
||||||
|
|
||||||
|
client.Put($"nodes/{Node}/qemu/{vmid}/config", diskParams);
|
||||||
|
|
||||||
|
// Add Cloud-Init drive
|
||||||
|
var cloudInitParams = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["ide2"] = $"{Storage}:cloudinit"
|
||||||
|
};
|
||||||
|
|
||||||
|
client.Put($"nodes/{Node}/qemu/{vmid}/config", cloudInitParams);
|
||||||
|
|
||||||
|
WriteVerbose("VM created successfully");
|
||||||
|
|
||||||
|
return GetVM(client, vmid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ConvertVMToTemplate(ProxmoxApiClient client, int vmid)
|
||||||
|
{
|
||||||
|
WriteVerbose($"Converting VM {vmid} to template...");
|
||||||
|
|
||||||
|
client.Post($"nodes/{Node}/qemu/{vmid}/template", new Dictionary<string, string>());
|
||||||
|
|
||||||
|
WriteVerbose("VM converted to template successfully");
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProxmoxVM GetVM(ProxmoxApiClient client, int vmid)
|
||||||
|
{
|
||||||
|
WriteVerbose($"Getting VM {vmid} details...");
|
||||||
|
|
||||||
|
var response = client.Get($"nodes/{Node}/qemu/{vmid}/config");
|
||||||
|
var config = JObject.Parse(response);
|
||||||
|
|
||||||
|
var vm = new ProxmoxVM
|
||||||
|
{
|
||||||
|
VMID = vmid,
|
||||||
|
Name = Name,
|
||||||
|
Node = Node,
|
||||||
|
Status = "stopped",
|
||||||
|
Template = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
WriteVerbose("VM details retrieved successfully");
|
||||||
|
|
||||||
|
return vm;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string FormatBytes(long bytes)
|
||||||
|
{
|
||||||
|
string[] suffixes = { "B", "KB", "MB", "GB", "TB" };
|
||||||
|
int counter = 0;
|
||||||
|
decimal number = bytes;
|
||||||
|
while (Math.Round(number / 1024) >= 1)
|
||||||
|
{
|
||||||
|
number = number / 1024;
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
return $"{number:n1} {suffixes[counter]}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Management.Automation;
|
using System.Management.Automation;
|
||||||
|
using PSProxmox.Client;
|
||||||
using PSProxmox.Session;
|
using PSProxmox.Session;
|
||||||
|
|
||||||
namespace PSProxmox.Cmdlets
|
namespace PSProxmox.Cmdlets
|
||||||
@@ -57,5 +58,17 @@ namespace PSProxmox.Cmdlets
|
|||||||
nameof(connection));
|
nameof(connection));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a Proxmox API client for the specified connection.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="connection">The connection to use. If null, the default connection will be used.</param>
|
||||||
|
/// <returns>A Proxmox API client.</returns>
|
||||||
|
protected ProxmoxApiClient GetProxmoxClient(ProxmoxConnection connection = null)
|
||||||
|
{
|
||||||
|
var conn = connection ?? GetConnection();
|
||||||
|
ValidateConnection(conn);
|
||||||
|
return new ProxmoxApiClient(conn, this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,151 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Management.Automation;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using PSProxmox.CloudImages;
|
||||||
|
|
||||||
|
namespace PSProxmox.Cmdlets
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="synopsis">Downloads a cloud image.</para>
|
||||||
|
/// <para type="description">The Save-ProxmoxCloudImage cmdlet downloads a cloud image from a repository.</para>
|
||||||
|
/// <para type="description">You can specify the distribution, release, and variant to download.</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <example>
|
||||||
|
/// <para>Download an Ubuntu 22.04 server cloud image</para>
|
||||||
|
/// <code>Save-ProxmoxCloudImage -Distribution "ubuntu" -Release "22.04" -Variant "server"</code>
|
||||||
|
/// </example>
|
||||||
|
/// <example>
|
||||||
|
/// <para>Download a Debian 11 generic cloud image</para>
|
||||||
|
/// <code>Save-ProxmoxCloudImage -Distribution "debian" -Release "11" -Variant "generic"</code>
|
||||||
|
/// </example>
|
||||||
|
[Cmdlet(VerbsData.Save, "ProxmoxCloudImage")]
|
||||||
|
[OutputType(typeof(string))]
|
||||||
|
public class SaveProxmoxCloudImageCmdlet : PSCmdlet
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The distribution to download (e.g., "ubuntu", "debian").</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = true, Position = 0)]
|
||||||
|
[ValidateSet("ubuntu", "debian")]
|
||||||
|
public string Distribution { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The release version to download (e.g., "22.04", "11").</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = true, Position = 1)]
|
||||||
|
public string Release { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The image variant to download (e.g., "server", "minimal").</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false, Position = 2)]
|
||||||
|
public string Variant { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The output path where the image will be saved. If not specified, the image will be saved to the default download directory.</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false)]
|
||||||
|
public string OutputPath { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">Force download even if the image already exists.</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false)]
|
||||||
|
public SwitchParameter Force { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Processes the cmdlet.
|
||||||
|
/// </summary>
|
||||||
|
protected override void ProcessRecord()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var task = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
// Get the cloud image
|
||||||
|
var image = await CloudImageRepository.GetImageAsync(Distribution, Release, Variant);
|
||||||
|
if (image == null)
|
||||||
|
{
|
||||||
|
throw new Exception($"Cloud image not found for {Distribution} {Release} {Variant}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a cancellation token source
|
||||||
|
using (var cts = new CancellationTokenSource())
|
||||||
|
{
|
||||||
|
// Download the image with progress reporting
|
||||||
|
var progressRecord = new ProgressRecord(
|
||||||
|
0,
|
||||||
|
$"Downloading {image.Filename}",
|
||||||
|
"0% complete");
|
||||||
|
|
||||||
|
var imagePath = await CloudImageDownloader.DownloadImageAsync(
|
||||||
|
image,
|
||||||
|
(bytesRead, totalBytes) =>
|
||||||
|
{
|
||||||
|
if (totalBytes > 0)
|
||||||
|
{
|
||||||
|
var percentComplete = (int)((double)bytesRead / totalBytes * 100);
|
||||||
|
progressRecord.PercentComplete = percentComplete;
|
||||||
|
progressRecord.StatusDescription = $"{percentComplete}% complete";
|
||||||
|
progressRecord.CurrentOperation = $"Downloaded {FormatBytes(bytesRead)} of {FormatBytes(totalBytes)}";
|
||||||
|
WriteProgress(progressRecord);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
progressRecord.StatusDescription = $"Downloaded {FormatBytes(bytesRead)}";
|
||||||
|
progressRecord.CurrentOperation = "Size unknown";
|
||||||
|
WriteProgress(progressRecord);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cts.Token);
|
||||||
|
|
||||||
|
// Complete the progress
|
||||||
|
progressRecord.RecordType = ProgressRecordType.Completed;
|
||||||
|
WriteProgress(progressRecord);
|
||||||
|
|
||||||
|
// Copy to output path if specified
|
||||||
|
if (!string.IsNullOrEmpty(OutputPath))
|
||||||
|
{
|
||||||
|
var outputDirectory = Path.GetDirectoryName(OutputPath);
|
||||||
|
if (!string.IsNullOrEmpty(outputDirectory) && !Directory.Exists(outputDirectory))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(outputDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
File.Copy(imagePath, OutputPath, true);
|
||||||
|
return OutputPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
return imagePath;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
task.Wait();
|
||||||
|
WriteObject(task.Result);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
WriteError(new ErrorRecord(
|
||||||
|
ex,
|
||||||
|
"SaveProxmoxCloudImageError",
|
||||||
|
ErrorCategory.NotSpecified,
|
||||||
|
null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string FormatBytes(long bytes)
|
||||||
|
{
|
||||||
|
string[] suffixes = { "B", "KB", "MB", "GB", "TB" };
|
||||||
|
int counter = 0;
|
||||||
|
decimal number = bytes;
|
||||||
|
while (Math.Round(number / 1024) >= 1)
|
||||||
|
{
|
||||||
|
number = number / 1024;
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
return $"{number:n1} {suffixes[counter]}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Management.Automation;
|
||||||
|
using System.Security;
|
||||||
|
using PSProxmox.Client;
|
||||||
|
using PSProxmox.Models;
|
||||||
|
using PSProxmox.Session;
|
||||||
|
|
||||||
|
namespace PSProxmox.Cmdlets
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="synopsis">Sets Cloud-Init configuration for a VM.</para>
|
||||||
|
/// <para type="description">The Set-ProxmoxVMCloudInit cmdlet sets Cloud-Init configuration for a VM.</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <example>
|
||||||
|
/// <para>Set Cloud-Init configuration for a VM</para>
|
||||||
|
/// <code>Set-ProxmoxVMCloudInit -Node "pve1" -VMID 100 -Username "admin" -Password (ConvertTo-SecureString "password" -AsPlainText -Force) -SSHKey "ssh-rsa AAAA..." -IPConfig "dhcp" -DNS "8.8.8.8,8.8.4.4"</code>
|
||||||
|
/// </example>
|
||||||
|
/// <example>
|
||||||
|
/// <para>Set Cloud-Init configuration with static IP</para>
|
||||||
|
/// <code>Set-ProxmoxVMCloudInit -Node "pve1" -VMID 100 -Username "admin" -Password (ConvertTo-SecureString "password" -AsPlainText -Force) -IPConfig "ip=192.168.1.100/24,gw=192.168.1.1"</code>
|
||||||
|
/// </example>
|
||||||
|
[Cmdlet(VerbsCommon.Set, "ProxmoxVMCloudInit")]
|
||||||
|
[OutputType(typeof(ProxmoxVM))]
|
||||||
|
public class SetProxmoxVMCloudInitCmdlet : ProxmoxCmdlet
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The node on which the VM is located.</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = true, Position = 0)]
|
||||||
|
public string Node { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The ID of the VM.</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = true, Position = 1)]
|
||||||
|
public int VMID { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The username for Cloud-Init.</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false)]
|
||||||
|
public string Username { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The password for Cloud-Init.</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false)]
|
||||||
|
public SecureString Password { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The SSH public key for Cloud-Init.</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false)]
|
||||||
|
public string SSHKey { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The IP configuration for Cloud-Init (e.g., "dhcp" or "ip=192.168.1.100/24,gw=192.168.1.1").</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false)]
|
||||||
|
public string IPConfig { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The DNS servers for Cloud-Init (comma-separated).</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false)]
|
||||||
|
public string DNS { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para type="description">The connection to the Proxmox VE server.</para>
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(Mandatory = false)]
|
||||||
|
public ProxmoxConnection Connection { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Processes the cmdlet.
|
||||||
|
/// </summary>
|
||||||
|
protected override void ProcessRecord()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var client = GetProxmoxClient(Connection);
|
||||||
|
|
||||||
|
var parameters = new Dictionary<string, string>();
|
||||||
|
|
||||||
|
// Add Cloud-Init parameters
|
||||||
|
if (!string.IsNullOrEmpty(Username))
|
||||||
|
{
|
||||||
|
parameters["ciuser"] = Username;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Password != null)
|
||||||
|
{
|
||||||
|
parameters["cipassword"] = ConvertSecureStringToString(Password);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(SSHKey))
|
||||||
|
{
|
||||||
|
parameters["sshkeys"] = Uri.EscapeDataString(SSHKey.Replace("\n", "\\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(IPConfig))
|
||||||
|
{
|
||||||
|
parameters["ipconfig0"] = IPConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(DNS))
|
||||||
|
{
|
||||||
|
parameters["nameserver"] = DNS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Cloud-Init configuration
|
||||||
|
WriteVerbose($"Setting Cloud-Init configuration for VM {VMID} on node {Node}...");
|
||||||
|
|
||||||
|
var response = client.Put($"nodes/{Node}/qemu/{VMID}/config", parameters);
|
||||||
|
|
||||||
|
WriteVerbose("Cloud-Init configuration set successfully");
|
||||||
|
|
||||||
|
// Get the VM details
|
||||||
|
var vmResponse = client.Get($"nodes/{Node}/qemu/{VMID}/status/current");
|
||||||
|
var vm = Newtonsoft.Json.JsonConvert.DeserializeObject<ProxmoxVM>(vmResponse);
|
||||||
|
|
||||||
|
WriteObject(vm);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
WriteError(new ErrorRecord(
|
||||||
|
ex,
|
||||||
|
"SetProxmoxVMCloudInitError",
|
||||||
|
ErrorCategory.NotSpecified,
|
||||||
|
null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ConvertSecureStringToString(SecureString secureString)
|
||||||
|
{
|
||||||
|
IntPtr valuePtr = IntPtr.Zero;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
valuePtr = System.Runtime.InteropServices.Marshal.SecureStringToGlobalAllocUnicode(secureString);
|
||||||
|
return System.Runtime.InteropServices.Marshal.PtrToStringUni(valuePtr);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
System.Runtime.InteropServices.Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -46,6 +46,8 @@
|
|||||||
<Compile Include="Cmdlets\GetProxmoxUserCmdlet.cs" />
|
<Compile Include="Cmdlets\GetProxmoxUserCmdlet.cs" />
|
||||||
<Compile Include="Cmdlets\GetProxmoxVMCmdlet.cs" />
|
<Compile Include="Cmdlets\GetProxmoxVMCmdlet.cs" />
|
||||||
<Compile Include="Cmdlets\GetProxmoxVMTemplateCmdlet.cs" />
|
<Compile Include="Cmdlets\GetProxmoxVMTemplateCmdlet.cs" />
|
||||||
|
<Compile Include="Cmdlets\GetProxmoxCloudImageCmdlet.cs" />
|
||||||
|
<Compile Include="Cmdlets\InvokeProxmoxCloudImageCustomizationCmdlet.cs" />
|
||||||
<Compile Include="Cmdlets\JoinProxmoxClusterCmdlet.cs" />
|
<Compile Include="Cmdlets\JoinProxmoxClusterCmdlet.cs" />
|
||||||
<Compile Include="Cmdlets\LeaveProxmoxClusterCmdlet.cs" />
|
<Compile Include="Cmdlets\LeaveProxmoxClusterCmdlet.cs" />
|
||||||
<Compile Include="Cmdlets\NewProxmoxClusterBackupCmdlet.cs" />
|
<Compile Include="Cmdlets\NewProxmoxClusterBackupCmdlet.cs" />
|
||||||
@@ -60,6 +62,9 @@
|
|||||||
<Compile Include="Cmdlets\NewProxmoxVMCmdlet.cs" />
|
<Compile Include="Cmdlets\NewProxmoxVMCmdlet.cs" />
|
||||||
<Compile Include="Cmdlets\NewProxmoxVMFromTemplateCmdlet.cs" />
|
<Compile Include="Cmdlets\NewProxmoxVMFromTemplateCmdlet.cs" />
|
||||||
<Compile Include="Cmdlets\NewProxmoxVMTemplateCmdlet.cs" />
|
<Compile Include="Cmdlets\NewProxmoxVMTemplateCmdlet.cs" />
|
||||||
|
<Compile Include="Cmdlets\NewProxmoxCloudImageTemplateCmdlet.cs" />
|
||||||
|
<Compile Include="Cmdlets\SaveProxmoxCloudImageCmdlet.cs" />
|
||||||
|
<Compile Include="Cmdlets\SetProxmoxVMCloudInitCmdlet.cs" />
|
||||||
<Compile Include="Cmdlets\RemoveProxmoxNetworkCmdlet.cs" />
|
<Compile Include="Cmdlets\RemoveProxmoxNetworkCmdlet.cs" />
|
||||||
<Compile Include="Cmdlets\RemoveProxmoxRoleCmdlet.cs" />
|
<Compile Include="Cmdlets\RemoveProxmoxRoleCmdlet.cs" />
|
||||||
<Compile Include="Cmdlets\RemoveProxmoxSDNVnetCmdlet.cs" />
|
<Compile Include="Cmdlets\RemoveProxmoxSDNVnetCmdlet.cs" />
|
||||||
@@ -73,6 +78,9 @@
|
|||||||
<Compile Include="Cmdlets\StartProxmoxVMCmdlet.cs" />
|
<Compile Include="Cmdlets\StartProxmoxVMCmdlet.cs" />
|
||||||
<Compile Include="Cmdlets\StopProxmoxVMCmdlet.cs" />
|
<Compile Include="Cmdlets\StopProxmoxVMCmdlet.cs" />
|
||||||
<Compile Include="Cmdlets\TestProxmoxConnectionCmdlet.cs" />
|
<Compile Include="Cmdlets\TestProxmoxConnectionCmdlet.cs" />
|
||||||
|
<Compile Include="CloudImages\CloudImageRepository.cs" />
|
||||||
|
<Compile Include="CloudImages\CloudImageDownloader.cs" />
|
||||||
|
<Compile Include="CloudImages\CloudImageCustomizer.cs" />
|
||||||
<Compile Include="IPAM\IPAMManager.cs" />
|
<Compile Include="IPAM\IPAMManager.cs" />
|
||||||
<Compile Include="Models\ProxmoxCluster.cs" />
|
<Compile Include="Models\ProxmoxCluster.cs" />
|
||||||
<Compile Include="Models\ProxmoxClusterBackup.cs" />
|
<Compile Include="Models\ProxmoxClusterBackup.cs" />
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Install the PSProxmox module to the current user's module directory
|
||||||
|
|
||||||
|
# Get the latest release
|
||||||
|
$releaseDir = Get-ChildItem -Path "$PSScriptRoot\..\Release" -Directory | Sort-Object Name -Descending | Select-Object -First 1
|
||||||
|
|
||||||
|
if (-not $releaseDir) {
|
||||||
|
Write-Error "No release found. Please build the module first."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$modulePath = "$env:USERPROFILE\Documents\WindowsPowerShell\Modules\PSProxmox"
|
||||||
|
|
||||||
|
# Create the module directory if it doesn't exist
|
||||||
|
if (-not (Test-Path $modulePath)) {
|
||||||
|
New-Item -Path $modulePath -ItemType Directory -Force | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Copy the module files
|
||||||
|
Copy-Item -Path "$PSScriptRoot\..\Module\*" -Destination $modulePath -Recurse -Force
|
||||||
|
|
||||||
|
Write-Host "PSProxmox module installed to $modulePath"
|
||||||
|
|
||||||
|
# Import the module
|
||||||
|
Import-Module PSProxmox -Force
|
||||||
|
|
||||||
|
# List the cloud image cmdlets
|
||||||
|
Get-Command -Module PSProxmox | Where-Object { $_.Name -like "*Cloud*" }
|
||||||
Reference in New Issue
Block a user