diff --git a/CHANGELOG.md b/CHANGELOG.md index d1c46c5..c1bf0eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 2.0.9 (2026-08-21) + +### Fixes + +- Suppressed stray output from the stackql binary PATH lookup. The `which`/`where` check inherited the terminal's stdout, so the resolved binary path was printed once per lookup (four times during `info`). The check now captures the child process output and only inspects the exit status. + ## 2.0.8 (2026-06-29) ### Features diff --git a/Cargo.lock b/Cargo.lock index 82b3240..9b45f76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1809,7 +1809,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "stackql-deploy" -version = "2.0.8" +version = "2.0.9" dependencies = [ "base64", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 1de764e..f522b1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "stackql-deploy" -version = "2.0.8" +version = "2.0.9" edition = "2021" rust-version = "1.75" description = "Infrastructure-as-code framework for declarative cloud resource management using StackQL" diff --git a/examples/azure/azure-web-server/README.md b/examples/azure/azure-web-server/README.md index ce6a150..a7325b2 100644 --- a/examples/azure/azure-web-server/README.md +++ b/examples/azure/azure-web-server/README.md @@ -24,13 +24,13 @@ flowchart LR | # | Resource | Provider Resource | Description | |---|----------|-------------------|-------------| -| 1 | `example_resource_group` | `azure.resources.resource_groups` | Resource group for all stack resources | +| 1 | `example_resource_group` | `azure.resource.resource_groups` | Resource group for all stack resources | | 2 | `example_vnet` | `azure.network.virtual_networks` | Virtual network with environment-specific CIDR | | 3 | `example_subnet` | `azure.network.subnets` | Subnet within the VNet | | 4 | `example_public_ip` | `azure.network.public_ip_addresses` | Static public IP for the VM | | 5 | `example_nsg` | `azure.network.network_security_groups` | NSG allowing HTTP (8080) and SSH (22 from VNet) | | 6 | `example_nic` | `azure.network.network_interfaces` | NIC with subnet, public IP, and NSG | -| 7 | `example_web_server` | `azure.compute.virtual_machines` | Ubuntu 18.04 VM (Standard_DS1_v2) | +| 7 | `example_web_server` | `azure.compute.virtual_machines` | Ubuntu 24.04 VM (size set by `hardwareProfile` in the manifest) | | 8 | `example_vm_ext` | `azure.compute.virtual_machine_extensions` | Custom script extension to start a web server | ## Environment-Specific CIDR Blocks diff --git a/examples/azure/azure-web-server/resources/example_nic.iql b/examples/azure/azure-web-server/resources/example_nic.iql index 27be6fc..f9c3d6f 100644 --- a/examples/azure/azure-web-server/resources/example_nic.iql +++ b/examples/azure/azure-web-server/resources/example_nic.iql @@ -1,35 +1,36 @@ -/*+ createorupdate */ -INSERT INTO azure.network.interfaces( - networkInterfaceName, - resourceGroupName, - subscriptionId, - data__location, - data__properties, - data__tags -) -SELECT - '{{ nic_name }}', - '{{ resource_group_name }}', - '{{ subscription_id }}', - '{{ location }}', - '{"ipConfigurations": [ {{ nic_ip_config }} ], "networkSecurityGroup": { "id": "{{ network_security_group_id }}"}}', - '{{ global_tags }}'; - -/*+ statecheck, retries=5, retry_delay=5 */ -SELECT COUNT(*) as count FROM azure.network.interfaces -WHERE subscriptionId = '{{ subscription_id }}' -AND resourceGroupName = '{{ resource_group_name }}' -AND networkInterfaceName = '{{ nic_name }}'; - -/*+ exports */ -SELECT id as network_interface_id -FROM azure.network.interfaces -WHERE subscriptionId = '{{ subscription_id }}' -AND resourceGroupName = '{{ resource_group_name }}' -AND networkInterfaceName = '{{ nic_name }}'; - -/*+ delete */ -DELETE FROM azure.network.interfaces -WHERE subscriptionId = '{{ subscription_id }}' -AND resourceGroupName = '{{ resource_group_name }}' -AND networkInterfaceName = '{{ nic_name }}'; +/*+ createorupdate */ +INSERT INTO azure.network.network_interfaces( + network_interface_name, + resource_group_name, + subscription_id, + location, + properties, + tags +) +SELECT + '{{ nic_name }}', + '{{ resource_group_name }}', + '{{ subscription_id }}', + '{{ location }}', + '{"ipConfigurations": [ {{ nic_ip_config }} ], "networkSecurityGroup": { "id": "{{ network_security_group_id }}"}}', + '{{ global_tags }}'; + +/*+ statecheck, retries=5, retry_delay=5 */ +SELECT COUNT(*) as count FROM azure.network.network_interfaces +WHERE subscription_id = '{{ subscription_id }}' +AND resource_group_name = '{{ resource_group_name }}' +AND network_interface_name = '{{ nic_name }}' +AND provisioning_state = 'Succeeded' + +/*+ exports */ +SELECT id as network_interface_id +FROM azure.network.network_interfaces +WHERE subscription_id = '{{ subscription_id }}' +AND resource_group_name = '{{ resource_group_name }}' +AND network_interface_name = '{{ nic_name }}' + +/*+ delete */ +DELETE FROM azure.network.network_interfaces +WHERE subscription_id = '{{ subscription_id }}' +AND resource_group_name = '{{ resource_group_name }}' +AND network_interface_name = '{{ nic_name }}' diff --git a/examples/azure/azure-web-server/resources/example_nsg.iql b/examples/azure/azure-web-server/resources/example_nsg.iql index 5d37386..cf2c9b4 100644 --- a/examples/azure/azure-web-server/resources/example_nsg.iql +++ b/examples/azure/azure-web-server/resources/example_nsg.iql @@ -1,36 +1,36 @@ -/*+ createorupdate */ -INSERT INTO azure.network.security_groups( - networkSecurityGroupName, - resourceGroupName, - subscriptionId, - data__location, - data__properties, - data__tags -) -SELECT - '{{ nsg_name }}', - '{{ resource_group_name }}', - '{{ subscription_id }}', - '{{ location }}', - '{"securityRules":{{ security_rules }}}', - '{{ global_tags }}'; - -/*+ statecheck, retries=5, retry_delay=5 */ -SELECT COUNT(*) as count FROM azure.network.security_groups -WHERE subscriptionId = '{{ subscription_id }}' -AND resourceGroupName = '{{ resource_group_name }}' -AND networkSecurityGroupName = '{{ nsg_name }}' -AND JSON_EXTRACT(properties, '$.securityRules') IS NOT NULL - -/*+ exports */ -SELECT id as network_security_group_id -FROM azure.network.security_groups -WHERE subscriptionId = '{{ subscription_id }}' -AND resourceGroupName = '{{ resource_group_name }}' -AND networkSecurityGroupName = '{{ nsg_name }}' - -/*+ delete */ -DELETE FROM azure.network.security_groups -WHERE subscriptionId = '{{ subscription_id }}' -AND resourceGroupName = '{{ resource_group_name }}' -AND networkSecurityGroupName = '{{ nsg_name }}' +/*+ createorupdate */ +INSERT INTO azure.network.network_security_groups( + network_security_group_name, + resource_group_name, + subscription_id, + location, + properties, + tags +) +SELECT + '{{ nsg_name }}', + '{{ resource_group_name }}', + '{{ subscription_id }}', + '{{ location }}', + '{"securityRules":{{ security_rules }}}', + '{{ global_tags }}'; + +/*+ statecheck, retries=5, retry_delay=5 */ +SELECT COUNT(*) as count FROM azure.network.network_security_groups +WHERE subscription_id = '{{ subscription_id }}' +AND resource_group_name = '{{ resource_group_name }}' +AND network_security_group_name = '{{ nsg_name }}' +AND security_rules IS NOT NULL + +/*+ exports */ +SELECT id as network_security_group_id +FROM azure.network.network_security_groups +WHERE subscription_id = '{{ subscription_id }}' +AND resource_group_name = '{{ resource_group_name }}' +AND network_security_group_name = '{{ nsg_name }}' + +/*+ delete */ +DELETE FROM azure.network.network_security_groups +WHERE subscription_id = '{{ subscription_id }}' +AND resource_group_name = '{{ resource_group_name }}' +AND network_security_group_name = '{{ nsg_name }}' diff --git a/examples/azure/azure-web-server/resources/example_public_ip.iql b/examples/azure/azure-web-server/resources/example_public_ip.iql index 9c5c53e..9c9617a 100644 --- a/examples/azure/azure-web-server/resources/example_public_ip.iql +++ b/examples/azure/azure-web-server/resources/example_public_ip.iql @@ -1,39 +1,39 @@ -/*+ createorupdate */ -INSERT INTO azure.network.public_ip_addresses( - publicIpAddressName, - resourceGroupName, - subscriptionId, - data__location, - data__sku, - data__properties, - data__tags -) -SELECT - '{{ public_ip_name }}', - '{{ resource_group_name }}', - '{{ subscription_id }}', - '{{ location }}', - '{"name":"Standard","tier":"Regional"}', - '{"publicIPAllocationMethod":"Static"}', - '{{ global_tags }}' - -/*+ statecheck, retries=5, retry_delay=5 */ -SELECT COUNT(*) as count FROM azure.network.public_ip_addresses -WHERE subscriptionId = '{{ subscription_id }}' -AND resourceGroupName = '{{ resource_group_name }}' -AND publicIpAddressName = '{{ public_ip_name }}' - -/*+ exports */ -SELECT '{{ public_ip_name }}' as public_ip_name, -JSON_EXTRACT(properties, '$.ipAddress') as public_ip_address, -id as public_ip_id -FROM azure.network.public_ip_addresses -WHERE subscriptionId = '{{ subscription_id }}' -AND resourceGroupName = '{{ resource_group_name }}' -AND publicIpAddressName = '{{ public_ip_name }}' - -/*+ delete */ -DELETE FROM azure.network.public_ip_addresses -WHERE subscriptionId = '{{ subscription_id }}' -AND resourceGroupName = '{{ resource_group_name }}' -AND publicIpAddressName = '{{ public_ip_name }}' +/*+ createorupdate */ +INSERT INTO azure.network.public_ip_addresses( + public_ip_address_name, + resource_group_name, + subscription_id, + location, + sku, + properties, + tags +) +SELECT + '{{ public_ip_name }}', + '{{ resource_group_name }}', + '{{ subscription_id }}', + '{{ location }}', + '{"name":"Standard","tier":"Regional"}', + '{"publicIPAllocationMethod":"Static"}', + '{{ global_tags }}' + +/*+ statecheck, retries=5, retry_delay=5 */ +SELECT COUNT(*) as count FROM azure.network.public_ip_addresses +WHERE subscription_id = '{{ subscription_id }}' +AND resource_group_name = '{{ resource_group_name }}' +AND public_ip_address_name = '{{ public_ip_name }}' + +/*+ exports */ +SELECT '{{ public_ip_name }}' as public_ip_name, +ip_address as public_ip_address, +id as public_ip_id +FROM azure.network.public_ip_addresses +WHERE subscription_id = '{{ subscription_id }}' +AND resource_group_name = '{{ resource_group_name }}' +AND public_ip_address_name = '{{ public_ip_name }}' + +/*+ delete */ +DELETE FROM azure.network.public_ip_addresses +WHERE subscription_id = '{{ subscription_id }}' +AND resource_group_name = '{{ resource_group_name }}' +AND public_ip_address_name = '{{ public_ip_name }}' diff --git a/examples/azure/azure-web-server/resources/example_resource_group.iql b/examples/azure/azure-web-server/resources/example_resource_group.iql index dc9c4b6..6084fae 100644 --- a/examples/azure/azure-web-server/resources/example_resource_group.iql +++ b/examples/azure/azure-web-server/resources/example_resource_group.iql @@ -1,31 +1,31 @@ -/*+ exists */ -SELECT COUNT(*) as count FROM azure.resources.resource_groups -WHERE subscriptionId = '{{ subscription_id }}' -AND resourceGroupName = '{{ resource_group_name }}' - -/*+ create */ -INSERT INTO azure.resources.resource_groups( - resourceGroupName, - subscriptionId, - data__location, - data__tags -) -SELECT - '{{ resource_group_name }}', - '{{ subscription_id }}', - '{{ location }}', - '{{ global_tags }}' - -/*+ statecheck, retries=5, retry_delay=5 */ -SELECT COUNT(*) as count FROM azure.resources.resource_groups -WHERE subscriptionId = '{{ subscription_id }}' -AND resourceGroupName = '{{ resource_group_name }}' -AND location = '{{ location }}' -AND JSON_EXTRACT(properties, '$.provisioningState') = 'Succeeded' - -/*+ exports */ -SELECT '{{ resource_group_name }}' as resource_group_name - -/*+ delete */ -DELETE FROM azure.resources.resource_groups -WHERE resourceGroupName = '{{ resource_group_name }}' AND subscriptionId = '{{ subscription_id }}' +/*+ exists */ +SELECT COUNT(*) as count FROM azure.resource.resource_groups +WHERE subscription_id = '{{ subscription_id }}' +AND resource_group_name = '{{ resource_group_name }}' + +/*+ create */ +INSERT INTO azure.resource.resource_groups( + resource_group_name, + subscription_id, + location, + tags +) +SELECT + '{{ resource_group_name }}', + '{{ subscription_id }}', + '{{ location }}', + '{{ global_tags }}' + +/*+ statecheck, retries=5, retry_delay=5 */ +SELECT COUNT(*) as count FROM azure.resource.resource_groups +WHERE subscription_id = '{{ subscription_id }}' +AND resource_group_name = '{{ resource_group_name }}' +AND location = '{{ location }}' +AND provisioning_state = 'Succeeded' + +/*+ exports */ +SELECT '{{ resource_group_name }}' as resource_group_name + +/*+ delete */ +DELETE FROM azure.resource.resource_groups +WHERE resource_group_name = '{{ resource_group_name }}' AND subscription_id = '{{ subscription_id }}' diff --git a/examples/azure/azure-web-server/resources/example_subnet.iql b/examples/azure/azure-web-server/resources/example_subnet.iql index fffb317..5b80bb9 100644 --- a/examples/azure/azure-web-server/resources/example_subnet.iql +++ b/examples/azure/azure-web-server/resources/example_subnet.iql @@ -1,38 +1,38 @@ -/*+ createorupdate */ -INSERT INTO azure.network.subnets( - subnetName, - virtualNetworkName, - resourceGroupName, - subscriptionId, - data__properties -) -SELECT - '{{ subnet_name }}', - '{{ vnet_name }}', - '{{ resource_group_name }}', - '{{ subscription_id }}', - '{"addressPrefix": "{{ subnet_cidr }}"}' - -/*+ statecheck, retries=5, retry_delay=5 */ -SELECT COUNT(*) as count FROM azure.network.subnets -WHERE subscriptionId = '{{ subscription_id }}' -AND resourceGroupName = '{{ resource_group_name }}' -AND virtualNetworkName = '{{ vnet_name }}' -AND subnetName = '{{ subnet_name }}' -AND JSON_EXTRACT(properties, '$.addressPrefix') = '{{ subnet_cidr }}' - -/*+ exports */ -SELECT '{{ subnet_name }}' as subnet_name, -id as subnet_id -FROM azure.network.subnets -WHERE subscriptionId = '{{ subscription_id }}' -AND resourceGroupName = '{{ resource_group_name }}' -AND virtualNetworkName = '{{ vnet_name }}' -AND subnetName = '{{ subnet_name }}' - -/*+ delete */ -DELETE FROM azure.network.subnets -WHERE subscriptionId = '{{ subscription_id }}' -AND resourceGroupName = '{{ resource_group_name }}' -AND virtualNetworkName = '{{ vnet_name }}' -AND subnetName = '{{ subnet_name }}' \ No newline at end of file +/*+ createorupdate */ +INSERT INTO azure.network.subnets( + subnet_name, + virtual_network_name, + resource_group_name, + subscription_id, + properties +) +SELECT + '{{ subnet_name }}', + '{{ vnet_name }}', + '{{ resource_group_name }}', + '{{ subscription_id }}', + '{"addressPrefix": "{{ subnet_cidr }}"}' + +/*+ statecheck, retries=5, retry_delay=5 */ +SELECT COUNT(*) as count FROM azure.network.subnets +WHERE subscription_id = '{{ subscription_id }}' +AND resource_group_name = '{{ resource_group_name }}' +AND virtual_network_name = '{{ vnet_name }}' +AND subnet_name = '{{ subnet_name }}' +AND address_prefix = '{{ subnet_cidr }}' + +/*+ exports */ +SELECT '{{ subnet_name }}' as subnet_name, +id as subnet_id +FROM azure.network.subnets +WHERE subscription_id = '{{ subscription_id }}' +AND resource_group_name = '{{ resource_group_name }}' +AND virtual_network_name = '{{ vnet_name }}' +AND subnet_name = '{{ subnet_name }}' + +/*+ delete */ +DELETE FROM azure.network.subnets +WHERE subscription_id = '{{ subscription_id }}' +AND resource_group_name = '{{ resource_group_name }}' +AND virtual_network_name = '{{ vnet_name }}' +AND subnet_name = '{{ subnet_name }}' diff --git a/examples/azure/azure-web-server/resources/example_vm_ext.iql b/examples/azure/azure-web-server/resources/example_vm_ext.iql index 6291d15..bf9f14c 100644 --- a/examples/azure/azure-web-server/resources/example_vm_ext.iql +++ b/examples/azure/azure-web-server/resources/example_vm_ext.iql @@ -1,36 +1,36 @@ -/*+ createorupdate */ -INSERT INTO azure.compute.virtual_machine_extensions( - resourceGroupName, - subscriptionId, - vmExtensionName, - vmName, - data__location, - data__properties, - data__tags -) -SELECT - '{{ resource_group_name }}', - '{{ subscription_id }}', - '{{ vm_ext_name }}', - '{{ vm_name }}', - '{{ location }}', - '{ "publisher": "Microsoft.Azure.Extensions", "type": "CustomScript", "typeHandlerVersion": "2.1", "settings": { "commandToExecute": "{{ command_to_execute }}"} }', - '{{ global_tags }}'; - -/*+ statecheck, retries=5, retry_delay=5 */ -SELECT COUNT(*) as count -FROM azure.compute.virtual_machine_extensions -WHERE subscriptionId = '{{ subscription_id }}' -AND resourceGroupName = '{{ resource_group_name }}' -AND vmExtensionName = '{{ vm_ext_name }}' -AND vmName = '{{ vm_name }}' - -/*+ exports */ -SELECT 'http://' || '{{ public_ip_address }}' || ':8080' as web_url - -/*+ delete */ -DELETE FROM azure.compute.virtual_machine_extensions -WHERE subscriptionId = '{{ subscription_id }}' -AND resourceGroupName = '{{ resource_group_name }}' -AND vmExtensionName = '{{ vm_ext_name }}' -AND vmName = '{{ vm_name }}' \ No newline at end of file +/*+ createorupdate */ +INSERT INTO azure.compute.virtual_machine_extensions( + resource_group_name, + subscription_id, + vm_extension_name, + vm_name, + location, + properties, + tags +) +SELECT + '{{ resource_group_name }}', + '{{ subscription_id }}', + '{{ vm_ext_name }}', + '{{ vm_name }}', + '{{ location }}', + '{ "publisher": "Microsoft.Azure.Extensions", "type": "CustomScript", "typeHandlerVersion": "2.1", "settings": { "commandToExecute": "{{ command_to_execute }}"} }', + '{{ global_tags }}'; + +/*+ statecheck, retries=5, retry_delay=5 */ +SELECT COUNT(*) as count +FROM azure.compute.virtual_machine_extensions +WHERE subscription_id = '{{ subscription_id }}' +AND resource_group_name = '{{ resource_group_name }}' +AND vm_extension_name = '{{ vm_ext_name }}' +AND vm_name = '{{ vm_name }}' + +/*+ exports */ +SELECT 'http://' || '{{ public_ip_address }}' || ':8080' as web_url + +/*+ delete */ +DELETE FROM azure.compute.virtual_machine_extensions +WHERE subscription_id = '{{ subscription_id }}' +AND resource_group_name = '{{ resource_group_name }}' +AND vm_extension_name = '{{ vm_ext_name }}' +AND vm_name = '{{ vm_name }}' diff --git a/examples/azure/azure-web-server/resources/example_vnet.iql b/examples/azure/azure-web-server/resources/example_vnet.iql index 55fc558..b26df88 100644 --- a/examples/azure/azure-web-server/resources/example_vnet.iql +++ b/examples/azure/azure-web-server/resources/example_vnet.iql @@ -1,33 +1,33 @@ -/*+ createorupdate */ -INSERT INTO azure.network.virtual_networks( - virtualNetworkName, - resourceGroupName, - subscriptionId, - data__location, - data__properties, - data__tags -) -SELECT - '{{ vnet_name }}', - '{{ resource_group_name }}', - '{{ subscription_id }}', - '{{ location }}', - '{"addressSpace": {"addressPrefixes":["{{ vnet_cidr_block }}"]}}', - '{{ global_tags }}' - -/*+ statecheck, retries=5, retry_delay=5 */ -SELECT COUNT(*) as count FROM azure.network.virtual_networks -WHERE subscriptionId = '{{ subscription_id }}' -AND resourceGroupName = '{{ resource_group_name }}' -AND virtualNetworkName = '{{ vnet_name }}' -AND JSON_EXTRACT(properties, '$.addressSpace.addressPrefixes[0]') = '{{ vnet_cidr_block }}' - -/*+ exports */ -SELECT '{{ vnet_name }}' as vnet_name, -'{{ vnet_cidr_block }}' as vnet_cidr_block - -/*+ delete */ -DELETE FROM azure.network.virtual_networks -WHERE subscriptionId = '{{ subscription_id }}' -AND resourceGroupName = '{{ resource_group_name }}' -AND virtualNetworkName = '{{ vnet_name }}' +/*+ createorupdate */ +INSERT INTO azure.network.virtual_networks( + virtual_network_name, + resource_group_name, + subscription_id, + location, + properties, + tags +) +SELECT + '{{ vnet_name }}', + '{{ resource_group_name }}', + '{{ subscription_id }}', + '{{ location }}', + '{"addressSpace": {"addressPrefixes":["{{ vnet_cidr_block }}"]}}', + '{{ global_tags }}' + +/*+ statecheck, retries=5, retry_delay=5 */ +SELECT COUNT(*) as count FROM azure.network.virtual_networks +WHERE subscription_id = '{{ subscription_id }}' +AND resource_group_name = '{{ resource_group_name }}' +AND virtual_network_name = '{{ vnet_name }}' +AND JSON_EXTRACT(address_space, '$.addressPrefixes[0]') = '{{ vnet_cidr_block }}' + +/*+ exports */ +SELECT '{{ vnet_name }}' as vnet_name, +'{{ vnet_cidr_block }}' as vnet_cidr_block + +/*+ delete */ +DELETE FROM azure.network.virtual_networks +WHERE subscription_id = '{{ subscription_id }}' +AND resource_group_name = '{{ resource_group_name }}' +AND virtual_network_name = '{{ vnet_name }}' diff --git a/examples/azure/azure-web-server/resources/example_web_server.iql b/examples/azure/azure-web-server/resources/example_web_server.iql index a069441..d937f33 100644 --- a/examples/azure/azure-web-server/resources/example_web_server.iql +++ b/examples/azure/azure-web-server/resources/example_web_server.iql @@ -1,36 +1,37 @@ -/*+ createorupdate */ -INSERT INTO azure.compute.virtual_machines( - resourceGroupName, - subscriptionId, - vmName, - data__location, - data__properties, - data__tags -) -SELECT - '{{ resource_group_name }}', - '{{ subscription_id }}', - '{{ vm_name }}', - '{{ location }}', - '{"hardwareProfile": {{ hardwareProfile }}, "storageProfile": {{ storageProfile }}, "osProfile": {{ osProfile }}, "networkProfile": {{ networkProfile }}}', - '{{ global_tags }}'; - -/*+ statecheck, retries=5, retry_delay=5 */ -SELECT COUNT(*) as count -FROM azure.compute.virtual_machines -WHERE subscriptionId = '{{ subscription_id }}' -AND resourceGroupName = '{{ resource_group_name }}' -AND vmName = '{{ vm_name }}' - -/*+ exports */ -SELECT id as vm_id, '{{ vm_name }}' as vm_name -FROM azure.compute.virtual_machines -WHERE subscriptionId = '{{ subscription_id }}' -AND resourceGroupName = '{{ resource_group_name }}' -AND vmName = '{{ vm_name }}' - -/*+ delete */ -DELETE FROM azure.compute.virtual_machines -WHERE subscriptionId = '{{ subscription_id }}' -AND resourceGroupName = '{{ resource_group_name }}' -AND vmName = '{{ vm_name }}' +/*+ createorupdate */ +INSERT INTO azure.compute.virtual_machines( + resource_group_name, + subscription_id, + vm_name, + location, + properties, + tags +) +SELECT + '{{ resource_group_name }}', + '{{ subscription_id }}', + '{{ vm_name }}', + '{{ location }}', + '{"hardwareProfile": {{ hardwareProfile }}, "storageProfile": {{ storageProfile }}, "osProfile": {{ osProfile }}, "networkProfile": {{ networkProfile }}}', + '{{ global_tags }}'; + +/*+ statecheck, retries=5, retry_delay=5 */ +SELECT COUNT(*) as count +FROM azure.compute.virtual_machines +WHERE subscription_id = '{{ subscription_id }}' +AND resource_group_name = '{{ resource_group_name }}' +AND vm_name = '{{ vm_name }}' +AND provisioning_state = 'Succeeded' + +/*+ exports */ +SELECT vm_id, '{{ vm_name }}' as vm_name +FROM azure.compute.virtual_machines +WHERE subscription_id = '{{ subscription_id }}' +AND resource_group_name = '{{ resource_group_name }}' +AND vm_name = '{{ vm_name }}' + +/*+ delete */ +DELETE FROM azure.compute.virtual_machines +WHERE subscription_id = '{{ subscription_id }}' +AND resource_group_name = '{{ resource_group_name }}' +AND vm_name = '{{ vm_name }}' diff --git a/examples/azure/azure-web-server/stackql_manifest.yml b/examples/azure/azure-web-server/stackql_manifest.yml index 831bbd2..28f67b8 100644 --- a/examples/azure/azure-web-server/stackql_manifest.yml +++ b/examples/azure/azure-web-server/stackql_manifest.yml @@ -2,7 +2,7 @@ version: 1 name: "azure-web-server" description: Provisions an Azure networking stack (resource group, VNet, subnet, NSG, public IP, NIC) with an Ubuntu VM running a web server. providers: - - azure::v24.10.00267 + - azure::v26.07.00420 globals: - name: subscription_id description: azure subscription id diff --git a/src/commands/teardown.rs b/src/commands/teardown.rs index a1ed05e..54e2c41 100644 --- a/src/commands/teardown.rs +++ b/src/commands/teardown.rs @@ -313,7 +313,7 @@ fn run_teardown(runner: &mut CommandRunner, dry_run: bool, show_queries: bool, _ // the delete query can reference them. if let Some(ref f) = fields { for (k, v) in f { - full_context.insert(format!("{}.{}", &resource.name, k), v.clone()); + full_context.insert(format!("{}.{}", resource.name, k), v.clone()); } } exists diff --git a/src/utils/binary.rs b/src/utils/binary.rs index 993b62d..a1841db 100644 --- a/src/utils/binary.rs +++ b/src/utils/binary.rs @@ -26,15 +26,19 @@ use std::path::PathBuf; use std::process::Command; /// Check if the stackql binary exists in PATH +/// +/// Uses `output()` rather than `status()` so the child's stdout/stderr are +/// captured instead of inherited - otherwise `which`/`where` print the +/// resolved path straight to the terminal on every lookup. pub fn binary_exists_in_path() -> bool { let binary_name = super::platform::get_binary_name(); - let status = if super::platform::get_platform() == super::platform::Platform::Windows { - Command::new("where").arg(&binary_name).status() + let output = if super::platform::get_platform() == super::platform::Platform::Windows { + Command::new("where").arg(&binary_name).output() } else { - Command::new("which").arg(&binary_name).status() + Command::new("which").arg(&binary_name).output() }; - status.map(|s| s.success()).unwrap_or(false) + output.map(|o| o.status.success()).unwrap_or(false) } /// Get the full path to the stackql binary