Repository: terraform-routeros/terraform-provider-routeros Branch: main Commit: 0d8c069c20a0 Files: 1284 Total size: 2.7 MB Directory structure: gitextract_v3herl8m/ ├── .devcontainer/ │ └── devcontainer.json ├── .gitattributes ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ ├── ci-pipeline-issue.md │ │ ├── documentation-update.md │ │ └── feature_request.md │ ├── dependabot.yml │ ├── scripts/ │ │ ├── setup_routeros.go │ │ └── setup_routeros.py │ └── workflows/ │ ├── go-mod-cache.yml │ ├── module_testing.yml │ └── release.yml ├── .gitignore ├── .goreleaser.yml ├── .releaserc ├── CHANGELOG.md ├── CODEOWNERS ├── LICENSE.md ├── Makefile ├── README.md ├── docs/ │ ├── data-sources/ │ │ ├── files.md │ │ ├── firewall.md │ │ ├── interface_bridge_filter.md │ │ ├── interfaces.md │ │ ├── ip_addresses.md │ │ ├── ip_arp.md │ │ ├── ip_dhcp_server_leases.md │ │ ├── ip_firewall.md │ │ ├── ip_routes.md │ │ ├── ip_services.md │ │ ├── ipv6_addresses.md │ │ ├── ipv6_firewall.md │ │ ├── system_resource.md │ │ ├── system_routerboard.md │ │ ├── wifi_easy_connect.md │ │ └── x509.md │ ├── guides/ │ │ ├── certificate_rotation.md │ │ ├── easy_import.md │ │ └── install_package.md │ ├── index.md │ └── resources/ │ ├── bridge.md │ ├── bridge_mlag.md │ ├── bridge_port.md │ ├── bridge_vlan.md │ ├── capsman_aaa.md │ ├── capsman_access_list.md │ ├── capsman_channel.md │ ├── capsman_configuration.md │ ├── capsman_datapath.md │ ├── capsman_interface.md │ ├── capsman_manager.md │ ├── capsman_manager_interface.md │ ├── capsman_provisioning.md │ ├── capsman_rates.md │ ├── capsman_security.md │ ├── certificate_scep_server.md │ ├── container.md │ ├── container_config.md │ ├── container_envs.md │ ├── container_mounts.md │ ├── dhcp_client.md │ ├── dhcp_client_option.md │ ├── dhcp_server.md │ ├── dhcp_server_lease.md │ ├── dhcp_server_network.md │ ├── disk_settings.md │ ├── dns.md │ ├── dns_record.md │ ├── file.md │ ├── firewall_addr_list.md │ ├── firewall_filter.md │ ├── firewall_mangle.md │ ├── firewall_nat.md │ ├── gre.md │ ├── identity.md │ ├── interface_6to4.md │ ├── interface_bonding.md │ ├── interface_bridge.md │ ├── interface_bridge_filter.md │ ├── interface_bridge_port.md │ ├── interface_bridge_settings.md │ ├── interface_bridge_vlan.md │ ├── interface_detect_internet.md │ ├── interface_dot1x_client.md │ ├── interface_dot1x_server.md │ ├── interface_eoip.md │ ├── interface_ethernet.md │ ├── interface_ethernet_switch.md │ ├── interface_ethernet_switch_crs.md │ ├── interface_ethernet_switch_crs_egress_vlan_tag.md │ ├── interface_ethernet_switch_crs_egress_vlan_translation.md │ ├── interface_ethernet_switch_crs_ingress_vlan_translation.md │ ├── interface_ethernet_switch_crs_vlan.md │ ├── interface_ethernet_switch_host.md │ ├── interface_ethernet_switch_port.md │ ├── interface_ethernet_switch_port_isolation.md │ ├── interface_ethernet_switch_rule.md │ ├── interface_ethernet_switch_vlan.md │ ├── interface_gre.md │ ├── interface_gre6.md │ ├── interface_ipip.md │ ├── interface_l2tp_client.md │ ├── interface_list.md │ ├── interface_list_member.md │ ├── interface_lte.md │ ├── interface_lte_apn.md │ ├── interface_macvlan.md │ ├── interface_ovpn_client.md │ ├── interface_ovpn_server.md │ ├── interface_pppoe_client.md │ ├── interface_pppoe_server.md │ ├── interface_sstp_client.md │ ├── interface_sstp_server.md │ ├── interface_veth.md │ ├── interface_vlan.md │ ├── interface_vrrp.md │ ├── interface_vxlan.md │ ├── interface_vxlan_vteps.md │ ├── interface_w60g.md │ ├── interface_w60g_station.md │ ├── interface_wireguard.md │ ├── interface_wireguard_peer.md │ ├── interface_wireless.md │ ├── interface_wireless_access_list.md │ ├── interface_wireless_cap.md │ ├── interface_wireless_connect_list.md │ ├── interface_wireless_security_profiles.md │ ├── ip_address.md │ ├── ip_cloud.md │ ├── ip_cloud_advanced.md │ ├── ip_dhcp_client.md │ ├── ip_dhcp_client_option.md │ ├── ip_dhcp_relay.md │ ├── ip_dhcp_server.md │ ├── ip_dhcp_server_config.md │ ├── ip_dhcp_server_lease.md │ ├── ip_dhcp_server_network.md │ ├── ip_dhcp_server_option.md │ ├── ip_dhcp_server_option_matcher.md │ ├── ip_dhcp_server_option_set.md │ ├── ip_dhcp_server_option_sets.md │ ├── ip_dns.md │ ├── ip_dns_adlist.md │ ├── ip_dns_forwarders.md │ ├── ip_dns_record.md │ ├── ip_firewall_addr_list.md │ ├── ip_firewall_connection_tracking.md │ ├── ip_firewall_filter.md │ ├── ip_firewall_layer7_protocol.md │ ├── ip_firewall_mangle.md │ ├── ip_firewall_nat.md │ ├── ip_firewall_raw.md │ ├── ip_hotspot.md │ ├── ip_hotspot_ip_binding.md │ ├── ip_hotspot_profile.md │ ├── ip_hotspot_service_port.md │ ├── ip_hotspot_user.md │ ├── ip_hotspot_user_profile.md │ ├── ip_hotspot_walled_garden.md │ ├── ip_hotspot_walled_garden_ip.md │ ├── ip_ipsec_identity.md │ ├── ip_ipsec_key.md │ ├── ip_ipsec_mode_config.md │ ├── ip_ipsec_peer.md │ ├── ip_ipsec_policy.md │ ├── ip_ipsec_policy_group.md │ ├── ip_ipsec_profile.md │ ├── ip_ipsec_proposal.md │ ├── ip_ipsec_settings.md │ ├── ip_nat_pmp.md │ ├── ip_nat_pmp_interfaces.md │ ├── ip_neighbor_discovery_settings.md │ ├── ip_pool.md │ ├── ip_route.md │ ├── ip_service.md │ ├── ip_settings.md │ ├── ip_smb.md │ ├── ip_ssh_server.md │ ├── ip_tftp.md │ ├── ip_tftp_settings.md │ ├── ip_traffic_flow.md │ ├── ip_traffic_flow_ipfix.md │ ├── ip_traffic_flow_target.md │ ├── ip_upnp.md │ ├── ip_upnp_interfaces.md │ ├── ip_vrf.md │ ├── ipip.md │ ├── ipv6_address.md │ ├── ipv6_dhcp_client.md │ ├── ipv6_dhcp_client_option.md │ ├── ipv6_dhcp_server.md │ ├── ipv6_dhcp_server_option.md │ ├── ipv6_dhcp_server_option_sets.md │ ├── ipv6_firewall_addr_list.md │ ├── ipv6_firewall_filter.md │ ├── ipv6_firewall_mangle.md │ ├── ipv6_firewall_nat.md │ ├── ipv6_nd_prefix.md │ ├── ipv6_neighbor_discovery.md │ ├── ipv6_pool.md │ ├── ipv6_route.md │ ├── ipv6_settings.md │ ├── move_items.md │ ├── ovpn_server.md │ ├── ppp_aaa.md │ ├── ppp_profile.md │ ├── ppp_secret.md │ ├── queue_simple.md │ ├── queue_tree.md │ ├── queue_type.md │ ├── radius.md │ ├── radius_incoming.md │ ├── routing_bfd_configuration.md │ ├── routing_bgp_connection.md │ ├── routing_bgp_evpn.md │ ├── routing_bgp_instance.md │ ├── routing_bgp_template.md │ ├── routing_bgp_vpn.md │ ├── routing_filter_rule.md │ ├── routing_id.md │ ├── routing_igmp_proxy_interface.md │ ├── routing_ospf_area.md │ ├── routing_ospf_area_range.md │ ├── routing_ospf_instance.md │ ├── routing_ospf_interface_template.md │ ├── routing_rule.md │ ├── routing_table.md │ ├── scheduler.md │ ├── snmp.md │ ├── snmp_community.md │ ├── system_certificate.md │ ├── system_certificate_scep_server.md │ ├── system_clock.md │ ├── system_identity.md │ ├── system_led.md │ ├── system_led_settings.md │ ├── system_logging.md │ ├── system_logging_action.md │ ├── system_note.md │ ├── system_ntp_client.md │ ├── system_ntp_server.md │ ├── system_routerboard_button_mode.md │ ├── system_routerboard_button_reset.md │ ├── system_routerboard_button_wps.md │ ├── system_routerboard_settings.md │ ├── system_routerboard_usb.md │ ├── system_scheduler.md │ ├── system_script.md │ ├── system_user.md │ ├── system_user_aaa.md │ ├── system_user_group.md │ ├── system_user_settings.md │ ├── system_user_sshkeys.md │ ├── tool_bandwidth_server.md │ ├── tool_email.md │ ├── tool_graphing_interface.md │ ├── tool_graphing_queue.md │ ├── tool_graphing_resource.md │ ├── tool_mac_server.md │ ├── tool_mac_server_ping.md │ ├── tool_mac_server_winbox.md │ ├── tool_netwatch.md │ ├── tool_sniffer.md │ ├── user_manager_advanced.md │ ├── user_manager_attribute.md │ ├── user_manager_database.md │ ├── user_manager_limitation.md │ ├── user_manager_profile.md │ ├── user_manager_profile_limitation.md │ ├── user_manager_router.md │ ├── user_manager_settings.md │ ├── user_manager_user.md │ ├── user_manager_user_group.md │ ├── user_manager_user_profile.md │ ├── vlan.md │ ├── vrrp.md │ ├── wifi.md │ ├── wifi_aaa.md │ ├── wifi_access_list.md │ ├── wifi_cap.md │ ├── wifi_capsman.md │ ├── wifi_channel.md │ ├── wifi_configuration.md │ ├── wifi_datapath.md │ ├── wifi_interworking.md │ ├── wifi_provisioning.md │ ├── wifi_security.md │ ├── wifi_security_multi_passphrase.md │ ├── wifi_steering.md │ ├── wireguard.md │ ├── wireguard_keys.md │ ├── wireguard_peer.md │ ├── zerotier.md │ ├── zerotier_controller.md │ └── zerotier_interface.md ├── examples/ │ ├── data-sources/ │ │ ├── routeros_files/ │ │ │ └── data-source.tf │ │ ├── routeros_interface_bridge_filter/ │ │ │ └── data-source.tf │ │ ├── routeros_interfaces/ │ │ │ └── data-source.tf │ │ ├── routeros_ip_addresses/ │ │ │ └── data-source.tf │ │ ├── routeros_ip_arp/ │ │ │ └── data-source.tf │ │ ├── routeros_ip_dhcp_server_leases/ │ │ │ └── data-source.tf │ │ ├── routeros_ip_firewall/ │ │ │ └── data-source.tf │ │ ├── routeros_ip_routes/ │ │ │ └── data-source.tf │ │ ├── routeros_ip_services/ │ │ │ └── data-source.tf │ │ ├── routeros_ipv6_addresses/ │ │ │ └── data-source.tf │ │ ├── routeros_system_resource/ │ │ │ └── data-source.tf │ │ ├── routeros_system_routerboard/ │ │ │ └── data-source.tf │ │ ├── routeros_wifi_easy_connect/ │ │ │ └── data-source.tf │ │ └── routeros_x509/ │ │ └── data-source.tf │ ├── provider/ │ │ └── provider.tf │ └── resources/ │ ├── routeros_bridge_mlag/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_capsman_aaa/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_capsman_access_list/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_capsman_channel/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_capsman_configuration/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_capsman_datapath/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_capsman_interface/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_capsman_manager/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_capsman_manager_interface/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_capsman_provisioning/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_capsman_rates/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_capsman_security/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_container/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_container_config/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_container_envs/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_container_mounts/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_disk_settings/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_file/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_6to4/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_bonding/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_bridge/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_bridge_filter/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_bridge_port/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_bridge_settings/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_bridge_vlan/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_detect_internet/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_dot1x_client/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_dot1x_server/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_eoip/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_ethernet/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_ethernet_switch/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_ethernet_switch_crs/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_ethernet_switch_crs_egress_vlan_tag/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_ethernet_switch_crs_egress_vlan_translation/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_ethernet_switch_crs_ingress_vlan_translation/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_ethernet_switch_crs_vlan/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_ethernet_switch_host/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_ethernet_switch_port/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_ethernet_switch_port_isolation/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_ethernet_switch_rule/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_ethernet_switch_vlan/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_gre/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_gre6/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_ipip/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_l2tp_client/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_l2tp_server/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_list/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_list_member/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_lte/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_lte_apn/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_macvlan/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_ovpn_server/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_pppoe_client/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_pppoe_server/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_veth/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_vlan/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_vrrp/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_vxlan/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_vxlan_vteps/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_w60g/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_w60g_station/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_wireguard/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_wireguard_peer/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_wireless/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_wireless_access_list/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_wireless_cap/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_wireless_connect_list/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_interface_wireless_security_profiles/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_address/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_cloud/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_cloud_advanced/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_dhcp_client/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_dhcp_client_option/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_dhcp_relay/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_dhcp_server/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_dhcp_server_config/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_dhcp_server_lease/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_dhcp_server_network/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_dhcp_server_option/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_dhcp_server_option_matcher/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_dhcp_server_option_sets/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_dns/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_dns_adlist/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_dns_forwarders/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_dns_record/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_firewall_addr_list/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_firewall_connection_tracking/ │ │ └── resource.tf │ ├── routeros_ip_firewall_filter/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_firewall_layer7_protocol/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_firewall_mangle/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_firewall_nat/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_hotspot/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_hotspot_ip_binding/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_hotspot_profile/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_hotspot_service_port/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_hotspot_user/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_hotspot_user_profile/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_hotspot_walled_garden/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_hotspot_walled_garden_ip/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_ipsec_identity/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_ipsec_key/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_ipsec_mode_config/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_ipsec_peer/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_ipsec_policy/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_ipsec_policy_group/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_ipsec_profile/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_ipsec_proposal/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_ipsec_settings/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_nat_pmp/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_nat_pmp_interfaces/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_neighbor_discovery_settings/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_pool/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_route/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_service/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_settings/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_smb/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_ssh_server/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_tftp/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_tftp_settings/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_traffic_flow/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_traffic_flow_ipfix/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_traffic_flow_target/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_upnp/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_upnp_interfaces/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ip_vrf/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ipv6_address/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ipv6_dhcp_client/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ipv6_dhcp_client_option/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ipv6_dhcp_server/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ipv6_dhcp_server_option/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ipv6_dhcp_server_option_sets/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ipv6_firewall_addr_list/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ipv6_firewall_filter/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ipv6_firewall_mangle/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ipv6_firewall_nat/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ipv6_nd_prefix/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ipv6_neighbor_discovery/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ipv6_pool/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ipv6_route/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ipv6_settings/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_move_items/ │ │ └── resource.tf │ ├── routeros_ovpn_server/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ppp_aaa/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ppp_profile/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_ppp_secret/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_queue_simple/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_queue_tree/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_queue_type/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_radius/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_radius_incoming/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_routing_bfd_configuration/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_routing_bgp_connection/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_routing_bgp_evpn/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_routing_bgp_instance/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_routing_bgp_template/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_routing_bgp_vpn/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_routing_filter_rule/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_routing_id/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_routing_igmp_proxy_interface/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_routing_ospf_area/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_routing_ospf_area_range/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_routing_ospf_instance/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_routing_ospf_interface_template/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_routing_rule/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_routing_table/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_snmp/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_snmp_community/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_system_certificate/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_system_certificate_scep_server/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_system_clock/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_system_identity/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_system_led/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_system_led_settings/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_system_logging/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_system_logging_actions/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_system_note/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_system_ntp_client/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_system_ntp_server/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_system_routerboard_button_mode/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_system_routerboard_button_reset/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_system_routerboard_button_wps/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_system_routerboard_settings/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_system_routerboard_usb/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_system_scheduler/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_system_script/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_system_user/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_system_user_aaa/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_system_user_group/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_system_user_settings/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_system_user_sshkeys/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_tool_bandwidth_server/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_tool_graphing_interface/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_tool_graphing_queue/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_tool_graphing_resource/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_tool_mac_server/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_tool_mac_server_ping/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_tool_mac_server_winbox/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_tool_netwatch/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_tool_sniffer/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_user_manager_advanced/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_user_manager_attribute/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_user_manager_database/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_user_manager_limitation/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_user_manager_profile/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_user_manager_profile_limitation/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_user_manager_router/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_user_manager_settings/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_user_manager_user/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_user_manager_user_group/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_user_manager_user_profile/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_wifi/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_wifi_aaa/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_wifi_access_list/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_wifi_cap/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_wifi_capsman/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_wifi_channel/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_wifi_configuration/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_wifi_datapath/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_wifi_interworking/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_wifi_provisioning/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_wifi_security/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_wifi_security_multi_passphrase/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_wifi_steering/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_wireguard_keys/ │ │ └── resource.tf │ ├── routeros_zerotier/ │ │ ├── import.sh │ │ └── resource.tf │ ├── routeros_zerotier_controller/ │ │ ├── import.sh │ │ └── resource.tf │ └── routeros_zerotier_interface/ │ ├── import.sh │ └── resource.tf ├── go.mod ├── go.sum ├── main.go ├── package.json ├── routeros/ │ ├── datasource_files.go │ ├── datasource_files_test.go │ ├── datasource_interface_bridge_filter.go │ ├── datasource_interface_bridge_filter_test.go │ ├── datasource_interfaces.go │ ├── datasource_interfaces_test.go │ ├── datasource_ip_address_test.go │ ├── datasource_ip_addresses.go │ ├── datasource_ip_arp.go │ ├── datasource_ip_arp_test.go │ ├── datasource_ip_dhcp_server_leases.go │ ├── datasource_ip_firewall.go │ ├── datasource_ip_firewall_addr_list.go │ ├── datasource_ip_firewall_filter.go │ ├── datasource_ip_firewall_mangle.go │ ├── datasource_ip_firewall_nat.go │ ├── datasource_ip_firewall_test.go │ ├── datasource_ip_routes.go │ ├── datasource_ip_routes_test.go │ ├── datasource_ip_services.go │ ├── datasource_ipv6_addresses.go │ ├── datasource_ipv6_addresses_test.go │ ├── datasource_ipv6_firewall.go │ ├── datasource_ipv6_firewall_filter.go │ ├── datasource_ipv6_firewall_mangle.go │ ├── datasource_ipv6_firewall_nat.go │ ├── datasource_ipv6_firewall_test.go │ ├── datasource_system_resource.go │ ├── datasource_system_resource_test.go │ ├── datasource_system_routerboard.go │ ├── datasource_system_routerboard_test.go │ ├── datasource_wifi_easy_connect.go │ ├── datasource_wifi_easy_connect_test.go │ ├── datasource_x509.go │ ├── datasource_x509_test.go │ ├── log.go │ ├── mikrotik.go │ ├── mikrotik_client.go │ ├── mikrotik_client_api.go │ ├── mikrotik_client_rest.go │ ├── mikrotik_client_transport_test.go │ ├── mikrotik_crud.go │ ├── mikrotik_resource_drift.go │ ├── mikrotik_resource_drift.yaml │ ├── mikrotik_resource_drift_implementation.go │ ├── mikrotik_serialize.go │ ├── mikrotik_serialize_test.go │ ├── mikrotik_test.go │ ├── parse_data_information_units.go │ ├── parse_data_information_units_test.go │ ├── parse_duration.go │ ├── parse_duration_test.go │ ├── parse_iprange.go │ ├── parse_iprange_test.go │ ├── provider.go │ ├── provider_resource_state_migration.go │ ├── provider_schema_helpers.go │ ├── provider_schema_helpers_test.go │ ├── provider_test.go │ ├── resource_actions.go │ ├── resource_actions_default.go │ ├── resource_actions_default_system.go │ ├── resource_actions_test.go │ ├── resource_capsman_access_list.go │ ├── resource_capsman_channel.go │ ├── resource_capsman_channel_test.go │ ├── resource_capsman_channel_v0.go │ ├── resource_capsman_channel_v1.go │ ├── resource_capsman_configuration.go │ ├── resource_capsman_configuration_test.go │ ├── resource_capsman_configuration_v0.go │ ├── resource_capsman_datapath.go │ ├── resource_capsman_datapath_test.go │ ├── resource_capsman_datapath_v0.go │ ├── resource_capsman_interface.go │ ├── resource_capsman_manager.go │ ├── resource_capsman_manager_test.go │ ├── resource_capsman_provisioning.go │ ├── resource_capsman_provisioning_test.go │ ├── resource_capsman_provisioning_v0.go │ ├── resource_capsman_rates.go │ ├── resource_capsman_rates_test.go │ ├── resource_capsman_rates_v0.go │ ├── resource_capsman_security.go │ ├── resource_capsman_security_test.go │ ├── resource_capsman_security_v0.go │ ├── resource_container.go │ ├── resource_container_config.go │ ├── resource_container_envs.go │ ├── resource_container_mounts.go │ ├── resource_disk_settings.go │ ├── resource_disk_settings_test.go │ ├── resource_file.go │ ├── resource_file_test.go │ ├── resource_interface_6to4.go │ ├── resource_interface_6to4_test.go │ ├── resource_interface_bonding.go │ ├── resource_interface_bonding_test.go │ ├── resource_interface_bridge.go │ ├── resource_interface_bridge_filter.go │ ├── resource_interface_bridge_filter_test.go │ ├── resource_interface_bridge_mlag _test.go │ ├── resource_interface_bridge_mlag.go │ ├── resource_interface_bridge_port.go │ ├── resource_interface_bridge_port_test.go │ ├── resource_interface_bridge_settings.go │ ├── resource_interface_bridge_settings_test.go │ ├── resource_interface_bridge_test.go │ ├── resource_interface_bridge_v0.go │ ├── resource_interface_bridge_vlan.go │ ├── resource_interface_bridge_vlan_test.go │ ├── resource_interface_bridge_vlan_v0.go │ ├── resource_interface_detect_internet.go │ ├── resource_interface_detect_internet_test.go │ ├── resource_interface_dot1x.go │ ├── resource_interface_dot1x_v0.go │ ├── resource_interface_eoip.go │ ├── resource_interface_eoip_v0.go │ ├── resource_interface_ethernet.go │ ├── resource_interface_ethernet_switch.go │ ├── resource_interface_ethernet_switch_crs.go │ ├── resource_interface_ethernet_switch_crs_egress_vlan_tag.go │ ├── resource_interface_ethernet_switch_crs_egress_vlan_translation.go │ ├── resource_interface_ethernet_switch_crs_ingress_vlan_translation.go │ ├── resource_interface_ethernet_switch_crs_vlan.go │ ├── resource_interface_ethernet_switch_host.go │ ├── resource_interface_ethernet_switch_host_test.go │ ├── resource_interface_ethernet_switch_port.go │ ├── resource_interface_ethernet_switch_port_isolation.go │ ├── resource_interface_ethernet_switch_port_isolation_test.go │ ├── resource_interface_ethernet_switch_port_test.go │ ├── resource_interface_ethernet_switch_rule.go │ ├── resource_interface_ethernet_switch_rule_test.go │ ├── resource_interface_ethernet_switch_test.go │ ├── resource_interface_ethernet_switch_vlan.go │ ├── resource_interface_ethernet_switch_vlan_test.go │ ├── resource_interface_ethernet_test.go │ ├── resource_interface_gre.go │ ├── resource_interface_gre6.go │ ├── resource_interface_gre6_test.go │ ├── resource_interface_gre_test.go │ ├── resource_interface_gre_v0.go │ ├── resource_interface_ipip.go │ ├── resource_interface_ipip_test.go │ ├── resource_interface_l2tp_client.go │ ├── resource_interface_l2tp_client_test.go │ ├── resource_interface_l2tp_server.go │ ├── resource_interface_l2tp_server_test.go │ ├── resource_interface_list.go │ ├── resource_interface_list_member.go │ ├── resource_interface_list_member_test.go │ ├── resource_interface_list_test.go │ ├── resource_interface_list_v0.go │ ├── resource_interface_lte.go │ ├── resource_interface_lte_apn.go │ ├── resource_interface_lte_apn_test.go │ ├── resource_interface_macvlan.go │ ├── resource_interface_macvlan_test.go │ ├── resource_interface_ovpn_server.go │ ├── resource_interface_pppoe_client.go │ ├── resource_interface_pppoe_client_test.go │ ├── resource_interface_pppoe_server.go │ ├── resource_interface_pppoe_server_test.go │ ├── resource_interface_sstp_client.go │ ├── resource_interface_sstp_server.go │ ├── resource_interface_veth.go │ ├── resource_interface_veth_test.go │ ├── resource_interface_vlan.go │ ├── resource_interface_vlan_test.go │ ├── resource_interface_vlan_v0.go │ ├── resource_interface_vrrp.go │ ├── resource_interface_vrrp_test.go │ ├── resource_interface_vrrp_v0.go │ ├── resource_interface_vxlan.go │ ├── resource_interface_vxlan_test.go │ ├── resource_interface_vxlan_vteps.go │ ├── resource_interface_vxlan_vteps_test.go │ ├── resource_interface_w60g.go │ ├── resource_interface_w60g_station.go │ ├── resource_interface_w60g_station_test.go │ ├── resource_interface_w60g_test.go │ ├── resource_interface_wireguard.go │ ├── resource_interface_wireguard_peer.go │ ├── resource_interface_wireguard_peer_test.go │ ├── resource_interface_wireguard_test.go │ ├── resource_interface_wireguard_v0.go │ ├── resource_interface_wireless.go │ ├── resource_interface_wireless_access_list.go │ ├── resource_interface_wireless_access_list_test.go │ ├── resource_interface_wireless_cap.go │ ├── resource_interface_wireless_connect_list.go │ ├── resource_interface_wireless_security_profiles.go │ ├── resource_interface_wireless_security_profiles_test.go │ ├── resource_interface_wireless_test.go │ ├── resource_ip_address.go │ ├── resource_ip_address_test.go │ ├── resource_ip_cloud.go │ ├── resource_ip_cloud_advanced.go │ ├── resource_ip_cloud_advanced_test.go │ ├── resource_ip_cloud_test.go │ ├── resource_ip_dhcp_client.go │ ├── resource_ip_dhcp_client_option.go │ ├── resource_ip_dhcp_client_option_test.go │ ├── resource_ip_dhcp_client_test.go │ ├── resource_ip_dhcp_relay.go │ ├── resource_ip_dhcp_relay_test.go │ ├── resource_ip_dhcp_server.go │ ├── resource_ip_dhcp_server_config.go │ ├── resource_ip_dhcp_server_lease.go │ ├── resource_ip_dhcp_server_lease_test.go │ ├── resource_ip_dhcp_server_network.go │ ├── resource_ip_dhcp_server_network_test.go │ ├── resource_ip_dhcp_server_network_v0.go │ ├── resource_ip_dhcp_server_option.go │ ├── resource_ip_dhcp_server_option_matcher.go │ ├── resource_ip_dhcp_server_option_sets.go │ ├── resource_ip_dhcp_server_option_sets_test.go │ ├── resource_ip_dhcp_server_option_test.go │ ├── resource_ip_dhcp_server_test.go │ ├── resource_ip_dhcp_server_v0.go │ ├── resource_ip_dns.go │ ├── resource_ip_dns_adlist.go │ ├── resource_ip_dns_adlist_test.go │ ├── resource_ip_dns_forwarders.go │ ├── resource_ip_dns_forwarders_test.go │ ├── resource_ip_dns_record.go │ ├── resource_ip_dns_record_test.go │ ├── resource_ip_dns_test.go │ ├── resource_ip_dns_v0.go │ ├── resource_ip_firewall_addr_list.go │ ├── resource_ip_firewall_addr_list_test.go │ ├── resource_ip_firewall_connection_tracking.go │ ├── resource_ip_firewall_connection_tracking_test.go │ ├── resource_ip_firewall_filter.go │ ├── resource_ip_firewall_filter_test.go │ ├── resource_ip_firewall_layer7_protocol.go │ ├── resource_ip_firewall_layer7_protocol_test.go │ ├── resource_ip_firewall_mangle.go │ ├── resource_ip_firewall_mangle_test.go │ ├── resource_ip_firewall_nat.go │ ├── resource_ip_firewall_nat_test.go │ ├── resource_ip_firewall_raw.go │ ├── resource_ip_firewall_raw_test.go │ ├── resource_ip_hotspot.go │ ├── resource_ip_hotspot_ip_binding.go │ ├── resource_ip_hotspot_ip_binding_test.go │ ├── resource_ip_hotspot_profile.go │ ├── resource_ip_hotspot_profile_test.go │ ├── resource_ip_hotspot_service_port.go │ ├── resource_ip_hotspot_service_port_test.go │ ├── resource_ip_hotspot_test.go │ ├── resource_ip_hotspot_user.go │ ├── resource_ip_hotspot_user_profile.go │ ├── resource_ip_hotspot_user_profile_test.go │ ├── resource_ip_hotspot_user_test.go │ ├── resource_ip_hotspot_walled_garden.go │ ├── resource_ip_hotspot_walled_garden_ip.go │ ├── resource_ip_hotspot_walled_garden_ip_test.go │ ├── resource_ip_hotspot_walled_garden_test.go │ ├── resource_ip_ipsec_identity.go │ ├── resource_ip_ipsec_identity_test.go │ ├── resource_ip_ipsec_key.go │ ├── resource_ip_ipsec_key_test.go │ ├── resource_ip_ipsec_mode_config.go │ ├── resource_ip_ipsec_mode_config_test.go │ ├── resource_ip_ipsec_peer.go │ ├── resource_ip_ipsec_peer_test.go │ ├── resource_ip_ipsec_policy.go │ ├── resource_ip_ipsec_policy_group.go │ ├── resource_ip_ipsec_policy_group_test.go │ ├── resource_ip_ipsec_policy_test.go │ ├── resource_ip_ipsec_profile.go │ ├── resource_ip_ipsec_profile_test.go │ ├── resource_ip_ipsec_proposal.go │ ├── resource_ip_ipsec_proposal_test.go │ ├── resource_ip_ipsec_settings.go │ ├── resource_ip_ipsec_settings_test.go │ ├── resource_ip_nat_pmp.go │ ├── resource_ip_nat_pmp_interfaces.go │ ├── resource_ip_nat_pmp_interfaces_test.go │ ├── resource_ip_nat_pmp_test.go │ ├── resource_ip_neighbor_discovery.go │ ├── resource_ip_neighbor_discovery_test.go │ ├── resource_ip_pool.go │ ├── resource_ip_pool_test.go │ ├── resource_ip_pool_v0.go │ ├── resource_ip_route.go │ ├── resource_ip_route_test.go │ ├── resource_ip_service.go │ ├── resource_ip_service_test.go │ ├── resource_ip_settings.go │ ├── resource_ip_settings_test.go │ ├── resource_ip_smb.go │ ├── resource_ip_smb_test.go │ ├── resource_ip_ssh_server.go │ ├── resource_ip_ssh_server_test.go │ ├── resource_ip_tftp.go │ ├── resource_ip_tftp_settings.go │ ├── resource_ip_tftp_test.go │ ├── resource_ip_traffic_flow.go │ ├── resource_ip_traffic_flow_ipfix.go │ ├── resource_ip_traffic_flow_ipfix_test.go │ ├── resource_ip_traffic_flow_target.go │ ├── resource_ip_traffic_flow_target_test.go │ ├── resource_ip_traffic_flow_test.go │ ├── resource_ip_upnp.go │ ├── resource_ip_upnp_interfaces.go │ ├── resource_ip_upnp_interfaces_test.go │ ├── resource_ip_upnp_test.go │ ├── resource_ip_vrf.go │ ├── resource_ip_vrf_test.go │ ├── resource_ipv6_address.go │ ├── resource_ipv6_address_test.go │ ├── resource_ipv6_dhcp_client.go │ ├── resource_ipv6_dhcp_client_option.go │ ├── resource_ipv6_dhcp_client_option_test.go │ ├── resource_ipv6_dhcp_client_test.go │ ├── resource_ipv6_dhcp_server.go │ ├── resource_ipv6_dhcp_server_option.go │ ├── resource_ipv6_dhcp_server_option_sets.go │ ├── resource_ipv6_dhcp_server_option_sets_test.go │ ├── resource_ipv6_dhcp_server_option_test.go │ ├── resource_ipv6_dhcp_server_test.go │ ├── resource_ipv6_firewall_addr_list.go │ ├── resource_ipv6_firewall_addr_list_test.go │ ├── resource_ipv6_firewall_filter.go │ ├── resource_ipv6_firewall_filter_test.go │ ├── resource_ipv6_firewall_mangle.go │ ├── resource_ipv6_firewall_mangle_test.go │ ├── resource_ipv6_firewall_nat.go │ ├── resource_ipv6_firewall_nat_test.go │ ├── resource_ipv6_nd_prefix.go │ ├── resource_ipv6_nd_prefix_test.go │ ├── resource_ipv6_neighbor_discovery.go │ ├── resource_ipv6_neighbor_discovery_test.go │ ├── resource_ipv6_pool.go │ ├── resource_ipv6_pool_test.go │ ├── resource_ipv6_route.go │ ├── resource_ipv6_route_test.go │ ├── resource_ipv6_settings.go │ ├── resource_ipv6_settings_test.go │ ├── resource_move.go │ ├── resource_ovpn_client.go │ ├── resource_ovpn_client_test.go │ ├── resource_ovpn_server.go │ ├── resource_ovpn_server_test.go │ ├── resource_ovpn_server_v0.go │ ├── resource_ppp_aaa.go │ ├── resource_ppp_aaa_test.go │ ├── resource_ppp_profile.go │ ├── resource_ppp_profile_test.go │ ├── resource_ppp_secret.go │ ├── resource_ppp_secret_test.go │ ├── resource_queue_simple.go │ ├── resource_queue_simple_test.go │ ├── resource_queue_tree.go │ ├── resource_queue_tree_test.go │ ├── resource_queue_type.go │ ├── resource_queue_type_test.go │ ├── resource_radius.go │ ├── resource_radius_v0.go │ ├── resource_routing_bfd_configuration.go │ ├── resource_routing_bfd_configuration_test.go │ ├── resource_routing_bgp_connection.go │ ├── resource_routing_bgp_connection_test.go │ ├── resource_routing_bgp_evpn.go │ ├── resource_routing_bgp_evpn_test.go │ ├── resource_routing_bgp_instance.go │ ├── resource_routing_bgp_instance_test.go │ ├── resource_routing_bgp_template.go │ ├── resource_routing_bgp_template_test.go │ ├── resource_routing_bgp_vpn.go │ ├── resource_routing_bgp_vpn_test.go │ ├── resource_routing_filter_rule.go │ ├── resource_routing_filter_rule_test.go │ ├── resource_routing_id.go │ ├── resource_routing_id_test.go │ ├── resource_routing_igmp_proxy_interface.go │ ├── resource_routing_igmp_proxy_interface_test.go │ ├── resource_routing_ospf_area.go │ ├── resource_routing_ospf_area_range.go │ ├── resource_routing_ospf_area_range_test.go │ ├── resource_routing_ospf_area_test.go │ ├── resource_routing_ospf_instance.go │ ├── resource_routing_ospf_instance_test.go │ ├── resource_routing_ospf_interface_template.go │ ├── resource_routing_ospf_interface_template_test.go │ ├── resource_routing_ospf_interface_template_v0.go │ ├── resource_routing_rule.go │ ├── resource_routing_rule_test.go │ ├── resource_routing_table.go │ ├── resource_routing_table_test.go │ ├── resource_snmp.go │ ├── resource_snmp_community.go │ ├── resource_snmp_community_test.go │ ├── resource_snmp_community_v0.go │ ├── resource_snmp_test.go │ ├── resource_system_certificate.go │ ├── resource_system_certificate_import_test.go │ ├── resource_system_certificate_scep_server.go │ ├── resource_system_certificate_scep_server_test.go │ ├── resource_system_certificate_test.go │ ├── resource_system_clock.go │ ├── resource_system_clock_test.go │ ├── resource_system_identity.go │ ├── resource_system_identity_test.go │ ├── resource_system_led.go │ ├── resource_system_led_settings.go │ ├── resource_system_logging.go │ ├── resource_system_logging_actions.go │ ├── resource_system_logging_actions_test.go │ ├── resource_system_logging_test.go │ ├── resource_system_note.go │ ├── resource_system_note_test.go │ ├── resource_system_ntp_client.go │ ├── resource_system_ntp_client_test.go │ ├── resource_system_ntp_server.go │ ├── resource_system_ntp_server_test.go │ ├── resource_system_routerboard_button_mode.go │ ├── resource_system_routerboard_button_reset.go │ ├── resource_system_routerboard_button_wps.go │ ├── resource_system_routerboard_settings.go │ ├── resource_system_routerboard_usb.go │ ├── resource_system_scheduler.go │ ├── resource_system_scheduler_test.go │ ├── resource_system_scheduler_v0.go │ ├── resource_system_script.go │ ├── resource_system_script_test.go │ ├── resource_system_user.go │ ├── resource_system_user_aaa.go │ ├── resource_system_user_aaa_test.go │ ├── resource_system_user_group.go │ ├── resource_system_user_group_test.go │ ├── resource_system_user_settings.go │ ├── resource_system_user_settings_test.go │ ├── resource_system_user_sshkeys.go │ ├── resource_system_user_sshkeys_test.go │ ├── resource_system_user_test.go │ ├── resource_testing_helper.go │ ├── resource_tool_bandwidth_server.go │ ├── resource_tool_bandwidth_server_test.go │ ├── resource_tool_email.go │ ├── resource_tool_email_test.go │ ├── resource_tool_graphing.go │ ├── resource_tool_graphing_test.go │ ├── resource_tool_mac_server.go │ ├── resource_tool_mac_server_test.go │ ├── resource_tool_netwatch.go │ ├── resource_tool_netwatch_test.go │ ├── resource_tool_sniffer.go │ ├── resource_tool_sniffer_test.go │ ├── resource_user_manager_advanced.go │ ├── resource_user_manager_attribute.go │ ├── resource_user_manager_database.go │ ├── resource_user_manager_limitation.go │ ├── resource_user_manager_profile.go │ ├── resource_user_manager_profile_limitation.go │ ├── resource_user_manager_router.go │ ├── resource_user_manager_settings.go │ ├── resource_user_manager_user.go │ ├── resource_user_manager_user_group.go │ ├── resource_user_manager_user_profile.go │ ├── resource_wifi.go │ ├── resource_wifi_aaa.go │ ├── resource_wifi_access_list.go │ ├── resource_wifi_cap.go │ ├── resource_wifi_capsman.go │ ├── resource_wifi_channel.go │ ├── resource_wifi_configuration.go │ ├── resource_wifi_datapath.go │ ├── resource_wifi_interworking.go │ ├── resource_wifi_provisioning.go │ ├── resource_wifi_security.go │ ├── resource_wifi_security_multi_passphrase.go │ ├── resource_wifi_steering.go │ ├── resource_wireguard_keys.go │ ├── resource_wireguard_keys_test.go │ ├── resource_zerotier.go │ ├── resource_zerotier_controller.go │ └── resource_zerotier_interface.go ├── templates/ │ ├── data-sources/ │ │ └── firewall.md.tmpl │ ├── data-sources.md.tmpl │ ├── guides/ │ │ ├── certificate_rotation.md │ │ ├── easy_import.md │ │ └── install_package.md │ ├── index.md.tmpl │ ├── resources/ │ │ ├── bridge.md.tmpl │ │ ├── bridge_port.md.tmpl │ │ ├── bridge_vlan.md.tmpl │ │ ├── certificate_scep_server.md.tmpl │ │ ├── dhcp_client.md.tmpl │ │ ├── dhcp_server.md.tmpl │ │ ├── dhcp_server_lease.md.tmpl │ │ ├── dhcp_server_network.md.tmpl │ │ ├── dns.md.tmpl │ │ ├── dns_record.md.tmpl │ │ ├── firewall_addr_list.md.tmpl │ │ ├── firewall_filter.md.tmpl │ │ ├── firewall_mangle.md.tmpl │ │ ├── firewall_nat.md.tmpl │ │ ├── gre.md.tmpl │ │ ├── identity.md.tmpl │ │ ├── ip_dhcp_server_option_set.md.tmpl │ │ ├── scheduler.md.tmpl │ │ ├── system_certificate.md.tmpl │ │ ├── vlan.md.tmpl │ │ ├── vrrp.md.tmpl │ │ ├── wireguard.md.tmpl │ │ └── wireguard_peer.md.tmpl │ └── resources.md.tmpl └── tools/ ├── boilerplate/ │ └── main.go ├── drift/ │ └── main.go ├── importer/ │ ├── logger.go │ ├── main.go │ ├── resource_aliases.go │ └── ssh.go ├── schema_changes/ │ ├── README.md │ └── main.go └── tools.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: .devcontainer/devcontainer.json ================================================ { "name": "Go", "image": "mcr.microsoft.com/devcontainers/go:2-1.24-bookworm", "customizations": { "vscode": { "extensions": [ "golang.go" ] } }, "mounts": [ "source=${env:HOME}${env:USERPROFILE}/.config,target=/home/vscode/.config,type=bind", "source=${env:HOME}${env:USERPROFILE}/.terraform.d,target=/home/vscode/.terraform.d,type=bind" ] } ================================================ FILE: .gitattributes ================================================ * text=auto eol=native ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Create a report to help us improve title: '' labels: bug assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior and the problem section from the TF file, without sensitive information. **Expected behavior** A clear and concise description of what you expected to happen. **Debug Information** If needed, be ready to publish debug output of the provider (green lines after executing `TF_LOG=debug ROS_LOG_COLOR=1 terraform `). **Stack Trace** If applicable, add the stack trace the crash produced. **Additional context** Add any other context about the problem here. ================================================ FILE: .github/ISSUE_TEMPLATE/ci-pipeline-issue.md ================================================ --- name: CI Pipeline issue about: Bugs with the CI pipeline, either release or testing title: '' labels: Non-functional assignees: '' --- **Which pipeline fails?:** **What job does the pipeline fail on?:** ================================================ FILE: .github/ISSUE_TEMPLATE/documentation-update.md ================================================ --- name: Documentation Update about: Updates and modifications to the documentation title: '' labels: documentation, Non-functional assignees: '' --- **What documentation needs updating?:** For example: - README - CHANGELOG - Provider **What needs to be added to the documentation?:** For example: Resource `routeros_interface_wireguard` needs to show how to set a public key. ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest an idea for this project title: '' labels: enhancement assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Documentation** If there is [documentation](https://help.mikrotik.com/docs/display/ROS/RouterOS) describing the new resource, add it. **Additional context** Add any other context or screenshots about the feature request here. ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "daily" time: "03:00" labels: - "dependencies" - package-ecosystem: gomod directory: / schedule: interval: daily labels: - "dependencies" ================================================ FILE: .github/scripts/setup_routeros.go ================================================ package main import ( "log" "os" "strings" "time" "github.com/go-routeros/routeros/v3" ) var ( commands = []string{ "/certificate/add name=ssl common-name=router key-size=prime256v1", "/certificate/sign .id=ssl", "/ip/service/set .id=www-ssl disabled=no certificate=ssl", "/ip/service/set .id=api-ssl disabled=no certificate=ssl", "/interface/bridge/add name=bridge", "/ip/pool/add name=dhcp ranges=192.168.88.100-192.168.88.200", "/interface/wireguard/add name=wg1", "/interface/list/add name=list", "/interface/print", } ) func main() { username := os.Getenv("ROS_USERNAME") password := os.Getenv("ROS_PASSWORD") host := os.Getenv("ROS_IP_ADDRESS") if username == "" { username = "admin" } if host == "" { if l := len(os.Args); l > 0 { host = os.Args[l-1] } else { log.Fatal("The IP address of the router must be set") } } var err error var client *routeros.Client for i := 0; i < 12; i++ { log.Printf("Connection attempt #%v... ", i) client, err = routeros.Dial(host+":8728", username, password) if err == nil { break } log.Println(err) time.Sleep(5 * time.Second) } if err != nil { log.Fatal("Host is not available") } defer client.Close() for _, command := range commands { cmd := strings.Split(command, " ") for i, c := range cmd { if strings.ContainsRune(c, '=') && len(c) > 0 && c[0] != '=' { cmd[i] = "=" + c } } log.Println(cmd) res, err := client.RunArgs(cmd) if err != nil { log.Fatal(err) } log.Println(res) } } ================================================ FILE: .github/scripts/setup_routeros.py ================================================ #!/usr/bin/env python3 import routeros_api import os def main(): user = os.environ.get("ROS_USERNAME") pswd = os.environ.get("ROS_PASSWORD") ip_addr = os.environ.get("ROS_IP_ADDRESS") connection = routeros_api.RouterOsApiPool( ip_addr, username=user, password=pswd, port=8728, plaintext_login=True) api = connection.get_api() # Set up bridge bridge = api.get_resource("/interface/bridge") bridge.add(name="bridge") # Set up certificates to allow use of REST API certificate = api.get_resource("/certificate") certificate.add(name="root-cert", common_name="MyRouter", days_valid="3650", key_usage="key-cert-sign,crl-sign") certificate.add(name="https-cert", common_name="MyRouter", days_valid="3650") certs = certificate.get() root_cert_id = [x['id'] for x in certs if x['name'] == "root-cert"][0] http_cert_id = [x['id'] for x in certs if x['name'] == "https-cert"][0] api.get_binary_resource("/").call("certificate/sign", {"id": bytes(root_cert_id, "utf-8")}) api.get_binary_resource("/").call("certificate/sign", {"id": bytes(http_cert_id, "utf-8"), "ca": b"root-cert"}) services = api.get_resource("/ip/service") for x in services.get(): if x['name'] in ['www-ssl', 'api-ssl']: services.set(id=x['id'],certificate="https-cert", disabled="false") # Create a DHCP pool pools = api.get_resource("/ip/pool") pools.add(name="dhcp", ranges="192.168.88.100-192.168.88.200") wireguard = api.get_resource("/interface/wireguard") wireguard.add(name="wg1") # Create a interface list list = api.get_resource("/interface/list") list.add(name="list") # Output the list of interfaces print(api.get_resource("/interface").get()) if __name__ == "__main__": main() ================================================ FILE: .github/workflows/go-mod-cache.yml ================================================ name: Go mod cache management concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true on: pull_request: branches: - "main" - "devel" paths: - "main.go" - "routeros/*.go" jobs: go-cache: name: Go Cache runs-on: ubuntu-latest steps: - name: Checkout the repo uses: actions/checkout@v6 - name: Setup Go environment uses: actions/setup-go@v6 with: go-version: 1.25 id: go - name: Get dependencies shell: bash run: go mod download all - name: Cache Go Modules uses: actions/cache@v5 with: path: | ~/.cache/go-build ~/go/pkg/mod key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go- ================================================ FILE: .github/workflows/module_testing.yml ================================================ name: Go Client Tests on: pull_request: branches: - 'main' - 'devel' paths: - 'main.go' - 'routeros/*.go' - '.github/workflows/*.yml' jobs: build: name: Build runs-on: ${{ matrix.os }} continue-on-error: ${{ matrix.experimental }} strategy: matrix: experimental: [false] os: [ubuntu-latest] routeros_version: - "7.12" - "7.15" - "7.16" steps: - name: Container check run: | echo "Wait for the container to finish launching..." while true; do docker logs ${{ job.services.routeros.id }} 2>&1 | grep MikroTik && break # docker logs ${{ job.services.routeros.id }} 2>&1 echo "waiting..." sleep 10 done - name: Check out code into the Go module directory uses: actions/checkout@v6 - name: Cache restore uses: actions/cache/restore@v5 with: path: | ~/.cache/go-build ~/go/pkg/mod key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go- - name: Setup Go environment uses: actions/setup-go@v6 with: go-version: 1.25 id: go - name: Get dependencies run: | go mod download - name: Build run: go build -v . - name: Preparing RouterOS for testing run: | go run .github/scripts/setup_routeros.go env: ROS_USERNAME: admin ROS_PASSWORD: '' ROS_IP_ADDRESS: 127.0.0.1 - name: Setup Terraform uses: hashicorp/setup-terraform@v4 - name: Run client tests run: go test -timeout 30m -v ./routeros env: ROS_HOSTURL: https://127.0.0.1 ROS_USERNAME: admin ROS_PASSWORD: '' ROS_INSECURE: true TF_ACC: 1 ROS_VERSION: ${{ matrix.routeros_version }} services: routeros: image: vaerhme/routeros:v${{ matrix.routeros_version }} ports: - 443:443 - 8728:8728 - 8729:8729 options: >- --cap-add=NET_ADMIN --entrypoint /routeros/entrypoint_with_four_interfaces.sh --device /dev/net/tun --device /dev/kvm ================================================ FILE: .github/workflows/release.yml ================================================ # This uses an action (crazy-max/ghaction-import-gpg) that assumes you set your # private key in the `GPG_PRIVATE_KEY` secret and passphrase in the `PASSPHRASE` # secret. If you would rather own your own GPG handling, please fork this action # or use an alternative one for key handling. # # You will need to pass the `--batch` flag to `gpg` in your signing step # in `goreleaser` to indicate this is being used in a non-interactive mode. # name: Release on: workflow_dispatch: push: branches: - 'main' - 'devel' tags-ignore: - '**' jobs: release: if: contains(github.event.head_commit.message, 'chore(release)') != true runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v6 with: fetch-depth: 0 # otherwise, you will failed to push refs to dest repo persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of your personal token token: ${{ secrets.ROUTEROS_ACTIONS }} - name: Import GPG key id: import_gpg uses: crazy-max/ghaction-import-gpg@v7 with: # These secrets will need to be configured for the repository: gpg_private_key: ${{ secrets.TERRAFORM_GPG_SIGNING_KEY }} passphrase: ${{ secrets.TERRAFORM_GPG_SIGN_KEY_PW }} - name: Release id: release uses: cycjimmy/semantic-release-action@v6 with: extra_plugins: | "@semantic-release/changelog@6.0.2" "@semantic-release/commit-analyzer@9.0.2" "@semantic-release/exec@6.0.3" "@semantic-release/git@10.0.1" "@semantic-release/github@8.0.7" "@semantic-release/release-notes-generator@10.0.3" "conventional-changelog-conventionalcommits@8.0.0" env: GITHUB_TOKEN: ${{ secrets.ROUTEROS_ACTIONS }} GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} - name: Set up Go if: steps.release.outputs.new_release_published == 'true' uses: actions/setup-go@v6 with: go-version: 1.25 - name: Run GoReleaser if: steps.release.outputs.new_release_published == 'true' uses: goreleaser/goreleaser-action@v7 with: version: latest args: release --clean env: GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} # GitHub sets this automatically GITHUB_TOKEN: ${{ secrets.ROUTEROS_ACTIONS }} ================================================ FILE: .gitignore ================================================ .vscode/* .idea pkg/* issues/* terraform-provider-routeros* terraform.tfstate* node_modules tools/boilerplate/examples/** tools/boilerplate/routeros/** tools/boilerplate/*.csv main.exe ================================================ FILE: .goreleaser.yml ================================================ version: 2 # Visit https://goreleaser.com for documentation on how to customize this # behavior. before: hooks: # this is just an example and not a requirement for provider building/publishing - go mod tidy builds: - env: # goreleaser does not work with CGO, it could also complicate # usage by users in CI/CD systems like Terraform Cloud where # they are unable to install libraries. - CGO_ENABLED=0 mod_timestamp: "{{ .CommitTimestamp }}" flags: - -trimpath ldflags: - "-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}" goos: - freebsd - windows - linux - darwin goarch: - amd64 - "386" - arm - arm64 ignore: - goos: darwin goarch: "386" binary: "{{ .ProjectName }}_v{{ .Version }}" archives: - format: zip name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}" checksum: name_template: "{{ .ProjectName }}_{{ .Version }}_SHA256SUMS" algorithm: sha256 signs: - artifacts: checksum args: # if you are using this is a GitHub action or some other automated pipeline, you # need to pass the batch flag to indicate its not interactive. - "--batch" - "--local-user" - "{{ .Env.GPG_FINGERPRINT }}" # set this environment variable for your signing key - "--output" - "${signature}" - "--detach-sign" - "${artifact}" release: prerelease: auto ================================================ FILE: .releaserc ================================================ { "branches": [ "main", { "name": "devel", "prerelease": true } ], "plugins": [ [ "@semantic-release/commit-analyzer", { "preset": "conventionalcommits", "releaseRules": [ { "scope": "no-release", "release": false } ] } ], [ "@semantic-release/release-notes-generator", { "preset": "conventionalcommits" } ], "@semantic-release/changelog", [ "@semantic-release/npm", { "npmPublish": false } ], [ "@semantic-release/exec", { "prepareCmd": "make docs" } ], [ "@semantic-release/git", { "assets": [ "package.json", "package-lock.json", "CHANGELOG.md", "docs", "routeros/mikrotik_resource_drift.go" ], "message": "chore(release): ${nextRelease.version}" } ], "@semantic-release/github" ] } ================================================ FILE: CHANGELOG.md ================================================ ## [1.100.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.99.1...v1.100.0) (2026-03-13) ### Features * **routing-id:** Add new resource `routeros_routing_id` ## [1.99.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.99.0...v1.99.1) (2026-03-08) ### Bug Fixes * **ipsec:** add IPSec missing algorithms ([3600b74](https://github.com/terraform-routeros/terraform-provider-routeros/commit/3600b744274239c8a19434f3538a04130bf79a38)) ## [1.99.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.98.0...v1.99.0) (2026-01-21) ### Features * **helpers:** Add a helper to compare values specified in bytes ([5f711f4](https://github.com/terraform-routeros/terraform-provider-routeros/commit/5f711f48173f8fef9f9709462ff76af881778681)) ### Bug Fixes * **container-config:** Add drift to the `ram_high` attribute ([f5cd755](https://github.com/terraform-routeros/terraform-provider-routeros/commit/f5cd7555fa11165f317172725d1cafe530c7fb10)) * **timeEqual:** Correct verification parameters ([e2f9696](https://github.com/terraform-routeros/terraform-provider-routeros/commit/e2f969686e89ac4e8fdb51c99d07d9c78341d5f9)) ## [1.98.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.97.0...v1.98.0) (2025-12-08) ### Features * **traffic-flow:** Add new resources around traffic flow ([07b839e](https://github.com/terraform-routeros/terraform-provider-routeros/commit/07b839e95b72f6d6c47206688ab9e882488d6ecd)), closes [#890](https://github.com/terraform-routeros/terraform-provider-routeros/issues/890) ## [1.97.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.96.0...v1.97.0) (2025-12-03) ### Features * **import:** Add import by resource identity ([bb91838](https://github.com/terraform-routeros/terraform-provider-routeros/commit/bb9183896be818adf467e29cdf839173a39a590f)), closes [#751](https://github.com/terraform-routeros/terraform-provider-routeros/issues/751) [#887](https://github.com/terraform-routeros/terraform-provider-routeros/issues/887) ## [1.96.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.95.0...v1.96.0) (2025-12-02) ### Features * **bgp-vpn:** Add new resource `routeros_routing_bgp_vpn` ([97a8755](https://github.com/terraform-routeros/terraform-provider-routeros/commit/97a8755cd4b7a70e440c12f07d80ff973370d2da)), closes [#884](https://github.com/terraform-routeros/terraform-provider-routeros/issues/884) ### Bug Fixes * **CRS:** Add lost attributes ([ce50482](https://github.com/terraform-routeros/terraform-provider-routeros/commit/ce50482e0ddeb8d7ff62215bf77cfd7ee7b66b60)) ## [1.95.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.94.0...v1.95.0) (2025-12-01) ### Features * **CRS:** Add new resource `routeros_interface_ethernet_switch_crs_vlan` ([83672bb](https://github.com/terraform-routeros/terraform-provider-routeros/commit/83672bba634efb90e74f4cac7cf643b2dfa7aded)), closes [#873](https://github.com/terraform-routeros/terraform-provider-routeros/issues/873) ### Bug Fixes * **ip-cloud:** Fix the names of skipped attributes ([863f474](https://github.com/terraform-routeros/terraform-provider-routeros/commit/863f4746052be622e360ec6a20ee8cbd65dbf1bd)), closes [#879](https://github.com/terraform-routeros/terraform-provider-routeros/issues/879) ## [1.94.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.93.0...v1.94.0) (2025-11-28) ### Features * **CRS:** Add resources to support the switch in CRS1xx and CRS2xx series devices ([5b1cd8e](https://github.com/terraform-routeros/terraform-provider-routeros/commit/5b1cd8e57741f802b8685924846b6948539c6c1c)) ### Bug Fixes * **CRS:** Add missing attribute ([c5ebaf5](https://github.com/terraform-routeros/terraform-provider-routeros/commit/c5ebaf5b6a84a6a46b2aeb2fc22c1b8b13c02705)) ## [1.93.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.92.1...v1.93.0) (2025-11-27) ### Features * **tools:** Add a router configuration importer ([5c10e4a](https://github.com/terraform-routeros/terraform-provider-routeros/commit/5c10e4a2b4db62af848db4ee8e19784c503a84db)) ### Bug Fixes * **file:** Permission denied for existing files ([f08077c](https://github.com/terraform-routeros/terraform-provider-routeros/commit/f08077cc31dfdbed6d16f3d621a0cb69caf26944)), closes [#812](https://github.com/terraform-routeros/terraform-provider-routeros/issues/812) * Missing attribute from various places ([badf405](https://github.com/terraform-routeros/terraform-provider-routeros/commit/badf405f161305006a62a2684ac4899e2fdee646)), closes [#860](https://github.com/terraform-routeros/terraform-provider-routeros/issues/860) ## [1.92.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.92.0...v1.92.1) (2025-11-18) ### Bug Fixes * routeros_interface_macvlan add missing attributes ([#865](https://github.com/terraform-routeros/terraform-provider-routeros/issues/865)) ([5b21944](https://github.com/terraform-routeros/terraform-provider-routeros/commit/5b21944a2ff183e8029b63ef468493bbcf1fa0ad)) ## [1.92.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.91.0...v1.92.0) (2025-11-18) ### Features * Add attributes to resouce ip_dhcp_server,interface_vlan … ([#861](https://github.com/terraform-routeros/terraform-provider-routeros/issues/861)) ([a88d0e1](https://github.com/terraform-routeros/terraform-provider-routeros/commit/a88d0e1ce67ca8ca3594fdacb1b710f5f01a64ed)) ## [1.91.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.90.0...v1.91.0) (2025-11-10) ### Features * **devcontainer:** add initial setup ([b4f9922](https://github.com/terraform-routeros/terraform-provider-routeros/commit/b4f992205736fee359f057b357f9c66cee3aa23f)) ## [1.90.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.89.0...v1.90.0) (2025-11-01) ### Features * **ip nat-pmp:** add examples ([174f0d1](https://github.com/terraform-routeros/terraform-provider-routeros/commit/174f0d158ec28ade47069ffe4fefad5198f5ba8f)) * **ip nat-pmp:** add resources ([3eaf59b](https://github.com/terraform-routeros/terraform-provider-routeros/commit/3eaf59b41dd56a20e2c09c0fa27d3083b95a4fff)) * **ospf area range:** Add new resource `routeros_routing_ospf_area_range` ([a336bd3](https://github.com/terraform-routeros/terraform-provider-routeros/commit/a336bd373908ac9f977a5462245470660f085cfa)), closes [#841](https://github.com/terraform-routeros/terraform-provider-routeros/issues/841) * update routeros_interface_ethernet to ROS 7.20 ([87dcd1f](https://github.com/terraform-routeros/terraform-provider-routeros/commit/87dcd1fb7b079f42908fe5d84320fce3aa538263)) * update routeros_ip_firewall_connection_tracking to ROS 7.20 ([d2bc43c](https://github.com/terraform-routeros/terraform-provider-routeros/commit/d2bc43c74b8bf6b5375cceedd8fb4b53e8a86e1c)) * update routeros_system_user_sshkeys to ROS 7.20 ([2f28d7b](https://github.com/terraform-routeros/terraform-provider-routeros/commit/2f28d7b5f94aa6b634db1d717e6c38ea4cb4dc27)) ### Bug Fixes * **container:** Add missing attributes ([9b78f48](https://github.com/terraform-routeros/terraform-provider-routeros/commit/9b78f48e12a4d681b3e114568a873742caa31aed)), closes [#839](https://github.com/terraform-routeros/terraform-provider-routeros/issues/839) * **contaner envs:** Change attribute `name` to `list` ([e004f90](https://github.com/terraform-routeros/terraform-provider-routeros/commit/e004f90339deeeb691b7c5cdbf41f64689652cdb)), closes [#856](https://github.com/terraform-routeros/terraform-provider-routeros/issues/856) * **interface l2tp client:** Add a new attribute `random_source_port` ([fc1c511](https://github.com/terraform-routeros/terraform-provider-routeros/commit/fc1c5119dcbb3335b5b329ef273d2b84b240367f)), closes [#836](https://github.com/terraform-routeros/terraform-provider-routeros/issues/836) * **ip nat-pmp interfaces:** fix property name ([46f95cf](https://github.com/terraform-routeros/terraform-provider-routeros/commit/46f95cf80d07679b5193d4dcc83b61a9cd0a1e19)) * **ip nat-pmp:** set min version for tests ([fa74e8a](https://github.com/terraform-routeros/terraform-provider-routeros/commit/fa74e8a5dbb3a4560f2e0f2b3d76d8bbba0e5dcf)) * **ip upnp:** typos ([fca8ff7](https://github.com/terraform-routeros/terraform-provider-routeros/commit/fca8ff7c8687513df7cf35db37df1121a1204348)) * **ospf interface template:** Add a new attribute `use-bfd` ([5a9b850](https://github.com/terraform-routeros/terraform-provider-routeros/commit/5a9b850b3dba817603356691d84e0af4972e9b0c)), closes [#837](https://github.com/terraform-routeros/terraform-provider-routeros/issues/837) * **pppoe client:** Add a new attribute `host-uniq` ([01379db](https://github.com/terraform-routeros/terraform-provider-routeros/commit/01379dbb56740dfcfd4a16b13e418008bc72b00f)), closes [#846](https://github.com/terraform-routeros/terraform-provider-routeros/issues/846) ## [1.89.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.88.1...v1.89.0) (2025-10-15) ### Features * Add support for EVPN-related resources ([bc250cb](https://github.com/terraform-routeros/terraform-provider-routeros/commit/bc250cbcc50eb1bcfcf49e587dbf0ccf7054a55c)), closes [#825](https://github.com/terraform-routeros/terraform-provider-routeros/issues/825) * **bfd:** Add new resource `routeros_routing_bfd_configuration` ([2d88357](https://github.com/terraform-routeros/terraform-provider-routeros/commit/2d88357667e8898cd2d5bba45b4d4abd4e77e061)), closes [#829](https://github.com/terraform-routeros/terraform-provider-routeros/issues/829) * Update `routeros_container` to ROS 7.20 ([8f10b7e](https://github.com/terraform-routeros/terraform-provider-routeros/commit/8f10b7ec13a01538bb85ac8549cd6d5dcebf41fa)) * Update `routeros_dhcp_server_network` to ROS 7.20 ([489e7e0](https://github.com/terraform-routeros/terraform-provider-routeros/commit/489e7e04f35681ab748e48b75fee21bd9514ec0d)) * Update `routeros_interface_veth` to ROS 7.20 ([d90e281](https://github.com/terraform-routeros/terraform-provider-routeros/commit/d90e28154a9518b2f6ce6752be531b0cd7e20de9)), closes [#826](https://github.com/terraform-routeros/terraform-provider-routeros/issues/826) * Update `routeros_interface_vrrp` to ROS 7.20 ([0065b69](https://github.com/terraform-routeros/terraform-provider-routeros/commit/0065b69ef3b141abcd7b0766971621f141da30b5)) * Update `routeros_interface_vxlan` to ROS 7.20 ([638e781](https://github.com/terraform-routeros/terraform-provider-routeros/commit/638e781630ed41e977e931e309e04253c28f8903)) * Update `routeros_ip_dhcp_client` to ROS 7.20 ([8e2f838](https://github.com/terraform-routeros/terraform-provider-routeros/commit/8e2f8381837934961a9646b67ca51bed56f407f8)) * Update `routeros_ip_firewall_connection_tracking` to ROS 7.20 ([1b3c77d](https://github.com/terraform-routeros/terraform-provider-routeros/commit/1b3c77d8181ad5746af1202b51a04a849c150194)) * Update `routeros_ip_firewall_nat` to ROS 7.20 ([ce43460](https://github.com/terraform-routeros/terraform-provider-routeros/commit/ce43460b5e13aeb0b2ae3709e4c5fbeffcf03bdf)) * Update `routeros_ipv6_dhcp_client` to ROS 7.20 ([f91acba](https://github.com/terraform-routeros/terraform-provider-routeros/commit/f91acba91e82e3ef57fd2eaf23f4cc1936405d0e)) * Update `routeros_ipv6_dhcp_server` to ROS 7.20 ([f11a08b](https://github.com/terraform-routeros/terraform-provider-routeros/commit/f11a08b2258043877db8c0c4bfd3291e1272a57a)) * Update `routeros_ppp_profile` to ROS 7.20 ([bacdbfe](https://github.com/terraform-routeros/terraform-provider-routeros/commit/bacdbfe4eb44f96bde84ef24d318137dc85f5e4c)) * Update `routeros_radius` to ROS 7.20 ([42a8c7e](https://github.com/terraform-routeros/terraform-provider-routeros/commit/42a8c7e1b523e6dbea404bff38b8255785c9ce38)) * Update `routeros_system_logging_action` to ROS 7.20 ([8d4543f](https://github.com/terraform-routeros/terraform-provider-routeros/commit/8d4543fd78edf4fae2cf411d91ad384f885171dc)) * Update `routeros_tool_netwatch` to ROS 7.20 ([837ebab](https://github.com/terraform-routeros/terraform-provider-routeros/commit/837ebab5a5ae206a3c46b920423b0b17a389cf40)) * Update `routeros_wifi_channel` to ROS 7.20 ([11ef93c](https://github.com/terraform-routeros/terraform-provider-routeros/commit/11ef93c8587aaf65bdb59296e7fd081f6d6a88e9)) * Update `routeros_wifi_configuration` to ROS 7.20 ([0a1614f](https://github.com/terraform-routeros/terraform-provider-routeros/commit/0a1614ff664f209cb882fd6507b783f544014770)) * Update `routeros_wifi` to ROS 7.20 ([61e821f](https://github.com/terraform-routeros/terraform-provider-routeros/commit/61e821f87dea7e4d5d3b403825551f1a30028b6e)) ### Bug Fixes * **capsman_interface:** Added processing of the `configuration.config` parameter and an extended resource import function ([e18ca7a](https://github.com/terraform-routeros/terraform-provider-routeros/commit/e18ca7a6bf25e8808897b54f8b801792da484c4b)), closes [#828](https://github.com/terraform-routeros/terraform-provider-routeros/issues/828) ## [1.88.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.88.0...v1.88.1) (2025-10-14) ### Bug Fixes * **cloud:** Missing fields at the `routeros_ip_cloud resource` ([ada8ea0](https://github.com/terraform-routeros/terraform-provider-routeros/commit/ada8ea0a750366dbfc2f0a98a8ffcef9dcb476c4)), closes [#820](https://github.com/terraform-routeros/terraform-provider-routeros/issues/820) ## [1.88.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.87.0...v1.88.0) (2025-09-26) ### Features * **firewall:** Add new resource `routeros_ip_firewall_layer7_protocol` ([6e49323](https://github.com/terraform-routeros/terraform-provider-routeros/commit/6e49323b5c9fe12f164e7d8e45873a6e4aab6cc0)), closes [#817](https://github.com/terraform-routeros/terraform-provider-routeros/issues/817) ## [1.87.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.86.3...v1.87.0) (2025-09-25) ### Features * **detect-internet:** Add new resource `routeros_interface_detect_internet` ([92aaa08](https://github.com/terraform-routeros/terraform-provider-routeros/commit/92aaa0832a95a55cc4d3b9fb9dfcc410d09d4b95)), closes [#808](https://github.com/terraform-routeros/terraform-provider-routeros/issues/808) ### Bug Fixes * **ipv6-firewall:** Wrong allowed values for `reject_with` field ([392ea04](https://github.com/terraform-routeros/terraform-provider-routeros/commit/392ea04e6e4894d105e043c54d9e8e4272c306ba)), closes [#810](https://github.com/terraform-routeros/terraform-provider-routeros/issues/810) ## [1.87.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.86.3...v1.87.0) (2025-09-25) ### Features * **detect-internet:** Add new resource `routeros_interface_detect_internet` ([92aaa08](https://github.com/terraform-routeros/terraform-provider-routeros/commit/92aaa0832a95a55cc4d3b9fb9dfcc410d09d4b95)), closes [#808](https://github.com/terraform-routeros/terraform-provider-routeros/issues/808) ### Bug Fixes * **ipv6-firewall:** Wrong allowed values for `reject_with` field ([392ea04](https://github.com/terraform-routeros/terraform-provider-routeros/commit/392ea04e6e4894d105e043c54d9e8e4272c306ba)), closes [#810](https://github.com/terraform-routeros/terraform-provider-routeros/issues/810) ## [1.86.3](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.86.2...v1.86.3) (2025-08-26) ### Bug Fixes * **interface_veth:** Add support for multiple addresses on the interface ([c6a656f](https://github.com/terraform-routeros/terraform-provider-routeros/commit/c6a656facc05a34f4cbbbb2fbba320e465cdfdd0)), closes [#804](https://github.com/terraform-routeros/terraform-provider-routeros/issues/804) * **wireless:** Support 2ghz-g/n band specification ([1647d25](https://github.com/terraform-routeros/terraform-provider-routeros/commit/1647d2574b8bfaf02598dde26ab470233409c8f0)) ## [1.86.2](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.86.1...v1.86.2) (2025-08-04) ### Bug Fixes * **container:** Remove mutually exclusive attributes ([d879a0c](https://github.com/terraform-routeros/terraform-provider-routeros/commit/d879a0c69b415a2e0ecc01071224f57555dba0de)), closes [#793](https://github.com/terraform-routeros/terraform-provider-routeros/issues/793) ## [1.86.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.86.0...v1.86.1) (2025-08-01) ### Bug Fixes * **container:** Add a reaction to the start/stop of a container from outside TF ([c46d5e6](https://github.com/terraform-routeros/terraform-provider-routeros/commit/c46d5e61a40234a72e57f16e992078cd33ed10d5)), closes [#793](https://github.com/terraform-routeros/terraform-provider-routeros/issues/793) ## [1.86.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.85.4...v1.86.0) (2025-07-27) ### Features * implement RouterOS DHCP server option matcher resource ([d459e68](https://github.com/terraform-routeros/terraform-provider-routeros/commit/d459e6891e325f320975213af72ae4971ec1f765)) * implement RouterOS TFTP resource ([2b98b84](https://github.com/terraform-routeros/terraform-provider-routeros/commit/2b98b8451f7fd5ebe3e03b4fcf338138182c52a5)) * implement RouterOS TFTP settings resource ([4533151](https://github.com/terraform-routeros/terraform-provider-routeros/commit/4533151c59044db366f8afe4bbc2d17da4fdb5c2)) * **ip_dns_forwarders:** Add new resource `routeros_ip_dns_forwarders` ([48be482](https://github.com/terraform-routeros/terraform-provider-routeros/commit/48be482cb6f562ae156404f8755c3f8aa4fb3778)), closes [#778](https://github.com/terraform-routeros/terraform-provider-routeros/issues/778) * **routing_igmp_proxy_interface:** Add support for IGMP Proxy ([4db8ffa](https://github.com/terraform-routeros/terraform-provider-routeros/commit/4db8ffad0d8233241cfad4b9321838c811b6e3a7)), closes [#773](https://github.com/terraform-routeros/terraform-provider-routeros/issues/773) ### Bug Fixes * `last_modified` missing on `routeros_files` datasource ([ffa72d3](https://github.com/terraform-routeros/terraform-provider-routeros/commit/ffa72d354291d50baef5ea5cf1ba0a0b70a6773e)), closes [#790](https://github.com/terraform-routeros/terraform-provider-routeros/issues/790) * **system:** Add missing default_mount_point_template attribute ([5e5bc6e](https://github.com/terraform-routeros/terraform-provider-routeros/commit/5e5bc6ee37efb8e0fedfa25a5940841cd0a4fdeb)) * **tool_email:** Add new fields ([c15047a](https://github.com/terraform-routeros/terraform-provider-routeros/commit/c15047a3d57d60a3de891d8ad7fdaeb9f1164ee0)), closes [#779](https://github.com/terraform-routeros/terraform-provider-routeros/issues/779) ## [1.85.4](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.85.3...v1.85.4) (2025-07-25) ### Bug Fixes * **address-list:** "context deadline exceeded" when working with firewall address-lists ([b450160](https://github.com/terraform-routeros/terraform-provider-routeros/commit/b450160ba65275aaad91815e71a5030ca29b61f6)), closes [#772](https://github.com/terraform-routeros/terraform-provider-routeros/issues/772) * routeros_ipv6_nd_prefix data source schema is missing "invalid" field ([e76a987](https://github.com/terraform-routeros/terraform-provider-routeros/commit/e76a98774da428aa27bb968c674b0de41983264f)) ## [1.85.3](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.85.2...v1.85.3) (2025-06-06) ### Bug Fixes * **bgp:** Add new filtering attributes ([bb8878d](https://github.com/terraform-routeros/terraform-provider-routeros/commit/bb8878d02ed043ebd1980bff125782ca2bd4999b)) * **interface-bonding:** Add a `lacp_mode` attribute ([684e81b](https://github.com/terraform-routeros/terraform-provider-routeros/commit/684e81ba2e2f1e44b04d49297a6edcd5b5aeb5f7)) * **interface-gre6:** Correct local IPv6 address validation ([30da790](https://github.com/terraform-routeros/terraform-provider-routeros/commit/30da790bfc372dc5258853ecd3c281e68a08e13e)), closes [#764](https://github.com/terraform-routeros/terraform-provider-routeros/issues/764) * **ip-dhcp-client:** Correct the `default_route_tables` attribute type to TypeSet ([93fe11c](https://github.com/terraform-routeros/terraform-provider-routeros/commit/93fe11c04a4ba27e0b50c45aec42c721aa293ff3)) * **ip-dhcp-server:** Add an `use_reconfigure` attribute ([db9bc30](https://github.com/terraform-routeros/terraform-provider-routeros/commit/db9bc308886e7cba71dbd037bda2bcf9c3c0db25)), closes [#749](https://github.com/terraform-routeros/terraform-provider-routeros/issues/749) * **ipv6-dhcp-client:** Add new attributes ([b8a1ff8](https://github.com/terraform-routeros/terraform-provider-routeros/commit/b8a1ff8b097b99cd0e5ed1ee738bde42563862fd)) * **tool-sniffer:** Add a `max_packet_size` attribute ([c0d06e0](https://github.com/terraform-routeros/terraform-provider-routeros/commit/c0d06e07c518342724ebfc668862e974e90d868f)) * **wifi-channel:** Add a `reselect-time` attribute ([28184c9](https://github.com/terraform-routeros/terraform-provider-routeros/commit/28184c91be5c50c9f9256d34f0c6858145c6c507)) * **wifi-datapath:** Add a `traffic_processing` attribute ([1de9d37](https://github.com/terraform-routeros/terraform-provider-routeros/commit/1de9d378b20bcd25adfcb3a32921a4407aa496f4)) ## [1.85.2](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.85.1...v1.85.2) (2025-06-04) ### Bug Fixes * **bridge_port:** Add new attributes (ROS 7.19) ([98c0dbf](https://github.com/terraform-routeros/terraform-provider-routeros/commit/98c0dbf479398a6f94c56530ddc05953d1123ba1)) * **bridge_port:** Skip counters ([3839756](https://github.com/terraform-routeros/terraform-provider-routeros/commit/38397561413974fc5616053ba8bc3de31fc50201)) * **bridge-mlag:** Add missing attributes to routeros_bridge_mlag ([47e1d26](https://github.com/terraform-routeros/terraform-provider-routeros/commit/47e1d267227a45bbda5ff666da30944e40737a85)), closes [#755](https://github.com/terraform-routeros/terraform-provider-routeros/issues/755) * **ip-service:** routeros_ip_service lead to Warning: Field 'proto' not found in the schema, Warning: Field 'dynamic' not found in the schema ([85dff9b](https://github.com/terraform-routeros/terraform-provider-routeros/commit/85dff9b58859a518d556ec2e015c613e5d2593c1)), closes [#756](https://github.com/terraform-routeros/terraform-provider-routeros/issues/756) ## [1.85.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.85.0...v1.85.1) (2025-05-27) ### Bug Fixes * **dhcp-client:** Add new attributes ([#753](https://github.com/terraform-routeros/terraform-provider-routeros/issues/753)) ([b2b8db0](https://github.com/terraform-routeros/terraform-provider-routeros/commit/b2b8db0b74fc1bc42a241fc7a6bc05137048bdb4)), closes [#749](https://github.com/terraform-routeros/terraform-provider-routeros/issues/749) * **firewall-v4:** Add Set/Unset attributes ([9886d39](https://github.com/terraform-routeros/terraform-provider-routeros/commit/9886d39b400b83521244f976b20fd2eb2da1844d)), closes [#748](https://github.com/terraform-routeros/terraform-provider-routeros/issues/748) * **wg-peers:** Fix drift attributes ([ba95824](https://github.com/terraform-routeros/terraform-provider-routeros/commit/ba95824334b192768be33c5337be9e8ff104f04c)), closes [#750](https://github.com/terraform-routeros/terraform-provider-routeros/issues/750) ## [1.85.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.84.0...v1.85.0) (2025-05-15) ### Features * **GRE v6:** Add new resource `routeros_interface_gre6` ([034cd10](https://github.com/terraform-routeros/terraform-provider-routeros/commit/034cd10e11cc6bb2f7ac5b2ff1158b8dfec55e60)), closes [#737](https://github.com/terraform-routeros/terraform-provider-routeros/issues/737) ### Bug Fixes * Fix IPSec resource input validation ([72063f5](https://github.com/terraform-routeros/terraform-provider-routeros/commit/72063f53af6a8108719d8137ab3284439d6e3beb)) * **logging_action changed:** Update for ROS 7.18 ([129f5fe](https://github.com/terraform-routeros/terraform-provider-routeros/commit/129f5feef2c349938cc09da5c952dca354aaf923)), closes [#735](https://github.com/terraform-routeros/terraform-provider-routeros/issues/735) ## [1.84.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.83.1...v1.84.0) (2025-05-06) ### Features * Implement duration check ([bf1a21d](https://github.com/terraform-routeros/terraform-provider-routeros/commit/bf1a21d89a98389a040bf296a3f261bfc365aae4)) * **ipv6:** Add new resource `routeros_ipv6_nd_prefix` ([476a70b](https://github.com/terraform-routeros/terraform-provider-routeros/commit/476a70bf59079c5c79582704ed648f40b9c10c9e)), closes [#730](https://github.com/terraform-routeros/terraform-provider-routeros/issues/730) ## [1.83.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.83.0...v1.83.1) (2025-04-28) ### Bug Fixes * **radius:** Remove server IP address validator ([6c486dd](https://github.com/terraform-routeros/terraform-provider-routeros/commit/6c486ddfaf71ee36271445d3b90adf3a6c8acafa)), closes [#726](https://github.com/terraform-routeros/terraform-provider-routeros/issues/726) * **sshkey:** Add `ForceNew` for the `key` attribute ([23f7c82](https://github.com/terraform-routeros/terraform-provider-routeros/commit/23f7c824b8069ce7fe13844aa0dab48fe7b1bb3c)), closes [#725](https://github.com/terraform-routeros/terraform-provider-routeros/issues/725) ## [1.83.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.82.0...v1.83.0) (2025-04-16) ### Features * **script:** Add a script start attribute ([e379996](https://github.com/terraform-routeros/terraform-provider-routeros/commit/e3799964205e4fd1623f7576604b7232ec454708)) ## [1.82.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.81.2...v1.82.0) (2025-04-15) ### Features * **queue:** Add new resource `routeros_queue_tree` ([54fe8a2](https://github.com/terraform-routeros/terraform-provider-routeros/commit/54fe8a274be5ea3ab89d28c6d1dbd624af85650a)) ## [1.81.2](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.81.1...v1.81.2) (2025-04-12) ### Bug Fixes * **ospf_area:** NSSA Translator/Translate Schema Error ([1348d32](https://github.com/terraform-routeros/terraform-provider-routeros/commit/1348d32589d3b508faa8e433ad3d925bb11cc012)), closes [#718](https://github.com/terraform-routeros/terraform-provider-routeros/issues/718) * routeros_interfaces data source schema is missing "inactive" field ([aeb11b4](https://github.com/terraform-routeros/terraform-provider-routeros/commit/aeb11b4d599a78c2837016bca943fd92a126e93a)) ## [1.81.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.81.0...v1.81.1) (2025-04-07) ### Bug Fixes * Resource `routeros_ip_route` schema is missing 2 fields ([c01edad](https://github.com/terraform-routeros/terraform-provider-routeros/commit/c01edad8a49130f154b1c9d5431bbe53415378f8)), closes [#712](https://github.com/terraform-routeros/terraform-provider-routeros/issues/712) ## [1.81.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.80.0...v1.81.0) (2025-03-30) ### Features * **ipv6_firewall_filter:** add action fasttrack-connection ([e0ca80c](https://github.com/terraform-routeros/terraform-provider-routeros/commit/e0ca80c93cedf8475f3816b3c52496d4ac3293f5)) ## [1.80.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.79.0...v1.80.0) (2025-03-21) ### Features * **ip-settings:** Add new resource `routeros_ip_settings` ([390f577](https://github.com/terraform-routeros/terraform-provider-routeros/commit/390f5775059a77eb7eea3e9b778251cffcf68275)), closes [#704](https://github.com/terraform-routeros/terraform-provider-routeros/issues/704) ### Bug Fixes * routeros_ip_hotspot_walled_garden_ip action validation may be incorrect ([3529224](https://github.com/terraform-routeros/terraform-provider-routeros/commit/3529224f637dcdaf713fa55529df2b84d7467a8d)), closes [#708](https://github.com/terraform-routeros/terraform-provider-routeros/issues/708) ## [1.79.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.78.0...v1.79.0) (2025-03-18) ### Features * **ip:** Add /ip/smb resource ([d05f4c0](https://github.com/terraform-routeros/terraform-provider-routeros/commit/d05f4c0a2c0b35e17ccca041047d5f0ce10d0b28)) * **system:** Add `/disk/settings` resource ([#697](https://github.com/terraform-routeros/terraform-provider-routeros/issues/697)) ([4193991](https://github.com/terraform-routeros/terraform-provider-routeros/commit/4193991203e54079d1dd8e0d1874965313163cc5)) * **tool:** Add /tool/graphing resources ([722108d](https://github.com/terraform-routeros/terraform-provider-routeros/commit/722108d2e16cab7e0cbcef7898899310c15f4113)) * **tool:** Add /tool/mac-server/ping resource ([31f9c50](https://github.com/terraform-routeros/terraform-provider-routeros/commit/31f9c509cfea8bf18dd852521c7f8f1297ef3c46)) ### Bug Fixes * **hotspot:** Add "radius_location_id" attribute to routeros_ip_hotspot_profile resource ([6d2dcbc](https://github.com/terraform-routeros/terraform-provider-routeros/commit/6d2dcbc768b09d68f0deb858cb39eba38cbdbcc4)), closes [#701](https://github.com/terraform-routeros/terraform-provider-routeros/issues/701) ## [1.78.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.77.3...v1.78.0) (2025-03-17) ### Features * **system:** Add /system/note resource ([6e588b3](https://github.com/terraform-routeros/terraform-provider-routeros/commit/6e588b319ecea978178cfc3fdc10af2dd74f7b1f)) ## [1.77.3](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.77.2...v1.77.3) (2025-03-12) ### Bug Fixes * **ethernet:** Ethernet `advertise` do not allow multiple values ([73823e5](https://github.com/terraform-routeros/terraform-provider-routeros/commit/73823e50db10cab5570d5dfac7d7fe1da7bee5d5)), closes [#692](https://github.com/terraform-routeros/terraform-provider-routeros/issues/692) * **ipv6-settings:** Add new attributes (ROS 7.18) ([32c8820](https://github.com/terraform-routeros/terraform-provider-routeros/commit/32c882048e875745e7f2c7105618933ce0d1b7f0)) * **ipv6-settings:** Skip metrics ([a8b3b33](https://github.com/terraform-routeros/terraform-provider-routeros/commit/a8b3b3388176de5189206a681516d96be28d1a3c)) ## [1.77.2](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.77.1...v1.77.2) (2025-03-05) ### Bug Fixes * **ip-cloud:** IP Cloud ddns_enabled 7.17 and newer ([d341c29](https://github.com/terraform-routeros/terraform-provider-routeros/commit/d341c29a2f400319b5dfc6891e8acbd46a0d312f)), closes [#686](https://github.com/terraform-routeros/terraform-provider-routeros/issues/686) * **ipv6-dhcp-client:** Add a new attribute (ROS 7.18) ([bcbd262](https://github.com/terraform-routeros/terraform-provider-routeros/commit/bcbd26240fbaf461d51e33bd57d8f889c23fe2e5)) * **ipv6-dhcp-client:** replace Default parameter with DiffSuppressFunc: AlwaysPresentNotUserProvided ([d5aad3b](https://github.com/terraform-routeros/terraform-provider-routeros/commit/d5aad3b66b03c01340295b151ca76ce927e5b26d)) ## [1.77.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.77.0...v1.77.1) (2025-03-04) ### Bug Fixes * **bandwidth-server:** Add new attributes (ROS 7.18) ([83d4bda](https://github.com/terraform-routeros/terraform-provider-routeros/commit/83d4bda0f738d404af67d92ee565e7826a08b77e)), closes [#683](https://github.com/terraform-routeros/terraform-provider-routeros/issues/683) * **dhcp-client:** Add a new attribute (ROS 7.18) ([253483c](https://github.com/terraform-routeros/terraform-provider-routeros/commit/253483cadea0cb9f7781e3c5727777d46792aa3a)), closes [#684](https://github.com/terraform-routeros/terraform-provider-routeros/issues/684) ## [1.77.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.76.7...v1.77.0) (2025-03-02) ### Features * **system:** Add user ssh-keys resource ([7c15569](https://github.com/terraform-routeros/terraform-provider-routeros/commit/7c15569b364162a1b1f37a7212ae29aa73a5c3d3)) ### Bug Fixes * **container:** Importing containers and container mounts (inconsistency and force recreate) ([af2585a](https://github.com/terraform-routeros/terraform-provider-routeros/commit/af2585a427479967aaf183980667f656455149ef)), closes [#652](https://github.com/terraform-routeros/terraform-provider-routeros/issues/652) * **dhcp-server:** 'Bad Request', details: 'unknown parameter src-address' ([3eb7c44](https://github.com/terraform-routeros/terraform-provider-routeros/commit/3eb7c44b07576a09bf4a164b7c46909c237c8dc1)), closes [#679](https://github.com/terraform-routeros/terraform-provider-routeros/issues/679) * minutes short name is lower m ([0379a1a](https://github.com/terraform-routeros/terraform-provider-routeros/commit/0379a1ac2c491683d0401ef1ac8d4241807c178f)) ## [1.76.7](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.76.6...v1.76.7) (2025-02-28) ### Bug Fixes * **bridge:** Add a `dynamic` attribute (ROS 7.18) ([cccee12](https://github.com/terraform-routeros/terraform-provider-routeros/commit/cccee12d85df6dae22e5d671d7a13c14acbb3025)), closes [#671](https://github.com/terraform-routeros/terraform-provider-routeros/issues/671) * **ip-pool:** Ignore new computed attributes (ROS 7.18) ([060d96e](https://github.com/terraform-routeros/terraform-provider-routeros/commit/060d96ecb71344797a8b0ee05a37f325bcfb7c99)), closes [#673](https://github.com/terraform-routeros/terraform-provider-routeros/issues/673) * **ipv6:** Add a `auto_link_local` attribute (ROS 7.18) ([c162cb0](https://github.com/terraform-routeros/terraform-provider-routeros/commit/c162cb0627f4d8487d33eb9169d85b489336e8a2)), closes [#672](https://github.com/terraform-routeros/terraform-provider-routeros/issues/672) * **logging:** Add new attributes (ROS 7.18) ([337bd74](https://github.com/terraform-routeros/terraform-provider-routeros/commit/337bd741f22d3494289471d9d4c6b1e4cd5f0224)), closes [#674](https://github.com/terraform-routeros/terraform-provider-routeros/issues/674) * **neighbor-discovery:** Add a `lldp_dcbx` attribute. ([0f3ea69](https://github.com/terraform-routeros/terraform-provider-routeros/commit/0f3ea6902e4e27882bf590a21e00b94b35023a7a)), closes [#670](https://github.com/terraform-routeros/terraform-provider-routeros/issues/670) ## [1.76.6](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.76.5...v1.76.6) (2025-02-19) ### Bug Fixes * Importing certificate and key without `routeros_file` resource ([8d3374e](https://github.com/terraform-routeros/terraform-provider-routeros/commit/8d3374e74fb2e25983fa58d4ef80dedda823396b)), closes [#660](https://github.com/terraform-routeros/terraform-provider-routeros/issues/660) ## [1.76.5](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.76.4...v1.76.5) (2025-02-17) ### Bug Fixes * **API:** Error: unknown RouterOS reply word: `!empty` (ROS 7.18) ([e1660aa](https://github.com/terraform-routeros/terraform-provider-routeros/commit/e1660aa3efdaebef3b1526b2af084adc8c75f515)), closes [#661](https://github.com/terraform-routeros/terraform-provider-routeros/issues/661) ## [1.76.4](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.76.3...v1.76.4) (2025-02-16) ### Bug Fixes * **wifi_security:** ft_mobility_domain is a (hex) string, not an integer ([497e430](https://github.com/terraform-routeros/terraform-provider-routeros/commit/497e4304192979d5f6d25b7220457e16dc3e1344)), closes [#662](https://github.com/terraform-routeros/terraform-provider-routeros/issues/662) ## [1.76.3](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.76.2...v1.76.3) (2025-02-13) ### Bug Fixes * Modify regex for RouterOS version ([b61d1db](https://github.com/terraform-routeros/terraform-provider-routeros/commit/b61d1db79433895027ae63002311ba062099c251)) * **routeros_version:** Rename the attribute ([51a4c7c](https://github.com/terraform-routeros/terraform-provider-routeros/commit/51a4c7cbb6b198c53742b382741f74547d67866d)), closes [#658](https://github.com/terraform-routeros/terraform-provider-routeros/issues/658) ## [1.76.2](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.76.1...v1.76.2) (2025-02-12) ### Bug Fixes * Fix the typo ([fa85c7b](https://github.com/terraform-routeros/terraform-provider-routeros/commit/fa85c7b624735dc618333bb251249d34e8bc5404)) * **queue:** Fix byte attribute comparison error ([9437e77](https://github.com/terraform-routeros/terraform-provider-routeros/commit/9437e77e8ada48393357a60ddcce4c7f064e6826)), closes [#643](https://github.com/terraform-routeros/terraform-provider-routeros/issues/643) ## [1.76.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.76.0...v1.76.1) (2025-02-11) ### Bug Fixes * **provider:** Add attribute drift handling between ROS versions ([32141a6](https://github.com/terraform-routeros/terraform-provider-routeros/commit/32141a622f24e903fa3e8601a16498714ac9573d)), closes [#654](https://github.com/terraform-routeros/terraform-provider-routeros/issues/654) ## [1.76.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.75.0...v1.76.0) (2025-02-04) ### Features * **queue:** Add new resource `routeros_queue_simple` ([b63640c](https://github.com/terraform-routeros/terraform-provider-routeros/commit/b63640ca0dea66e8d7863652508d6fa065f1ef64)), closes [#643](https://github.com/terraform-routeros/terraform-provider-routeros/issues/643) * **queue:** Add new resource `routeros_queue_type` ([45aadfb](https://github.com/terraform-routeros/terraform-provider-routeros/commit/45aadfbf6ab6cffc6c960399c9bf584e975e9466)), closes [#643](https://github.com/terraform-routeros/terraform-provider-routeros/issues/643) ### Bug Fixes * **dhcp:** Update for ROS 7.17 ([9b6a238](https://github.com/terraform-routeros/terraform-provider-routeros/commit/9b6a238b5bb9ceae1dbaeb45215a1fe804f005b8)), closes [#644](https://github.com/terraform-routeros/terraform-provider-routeros/issues/644) * Missing fields in the schema with version 7.17.1 ([8199e43](https://github.com/terraform-routeros/terraform-provider-routeros/commit/8199e4339f7e5917c25d28c7028f940a16d52df8)), closes [#650](https://github.com/terraform-routeros/terraform-provider-routeros/issues/650) * **w60:** Fix bugs after the first testing ([b1264e7](https://github.com/terraform-routeros/terraform-provider-routeros/commit/b1264e73cdd48d75d4a98e755fe38817eac4d6c9)), closes [#618](https://github.com/terraform-routeros/terraform-provider-routeros/issues/618) ## [1.75.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.74.0...v1.75.0) (2025-01-17) ### Features * add `resource_interface_sstp_*` resources ([219153c](https://github.com/terraform-routeros/terraform-provider-routeros/commit/219153c1b36a268af396d122c66cde75096ce8ff)) ### Bug Fixes * datasource routeros_interface_bridge_filter ([00c3ad3](https://github.com/terraform-routeros/terraform-provider-routeros/commit/00c3ad3fc59a6e25917d59a958db01e1d83d822d)) * **ip_dhcp_server_lease:** class_id should be computed ([c11c3c4](https://github.com/terraform-routeros/terraform-provider-routeros/commit/c11c3c462f10ad308f27afb020c49a7f616e68b8)) * **ipv6_firewall_net:** ipv6_firewall_nat has to_address not to_addresses ([c77d027](https://github.com/terraform-routeros/terraform-provider-routeros/commit/c77d027ff196a8a72e0fee26a8b9b14ebd0089b0)) * Remove DiffSuppress for `multi_passphrase_group` ([367d5e4](https://github.com/terraform-routeros/terraform-provider-routeros/commit/367d5e47e3e9b1aedfa63b6e5fafc0eb440734ba)), closes [#630](https://github.com/terraform-routeros/terraform-provider-routeros/issues/630) ## [1.74.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.73.0...v1.74.0) (2025-01-03) ### Features * **l2tp:** Add new resource `routeros_interface_l2tp_client` ([f2d3f9c](https://github.com/terraform-routeros/terraform-provider-routeros/commit/f2d3f9c372774c69f6edb9e55df5699a3117390f)), closes [#629](https://github.com/terraform-routeros/terraform-provider-routeros/issues/629) ### Bug Fixes * use DiffSuppressFunc: AlwaysPresentNotUserProvided ([931752c](https://github.com/terraform-routeros/terraform-provider-routeros/commit/931752c20b9b5caaec92391457150dbc001786c0)) ## [1.73.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.72.0...v1.73.0) (2024-12-30) ### Features * **wifi:** Add new resource `routeros_wifi_security_multi_passphrase` ([4ef710a](https://github.com/terraform-routeros/terraform-provider-routeros/commit/4ef710a25713aa9b5b6faa15eb164f8f9e152205)), closes [#621](https://github.com/terraform-routeros/terraform-provider-routeros/issues/621) * **wifi:** Add new resources `routeros_interface_w60g`, `routeros_interface_w60g_station` ([746a223](https://github.com/terraform-routeros/terraform-provider-routeros/commit/746a2235ed90b76eab38f0a8f8ef375d96cde5c9)), closes [#618](https://github.com/terraform-routeros/terraform-provider-routeros/issues/618) ### Bug Fixes * **bridge_filter:** Missing field in interface_bridge_filter ([63474c8](https://github.com/terraform-routeros/terraform-provider-routeros/commit/63474c834b520591602299e13ae0f7c783367dc8)), closes [#620](https://github.com/terraform-routeros/terraform-provider-routeros/issues/620) * **ip_dhcp_relay:** Field 'dhcp_server_vrf' not found in the schema ([f2f12b0](https://github.com/terraform-routeros/terraform-provider-routeros/commit/f2f12b0603685795097b32aa9c4bb4344e44f200)), closes [#622](https://github.com/terraform-routeros/terraform-provider-routeros/issues/622) * **wifi:** Using `5ghz-an` for `routeros_wifi_channel` returns status code `400` ([d603682](https://github.com/terraform-routeros/terraform-provider-routeros/commit/d603682702b60e56f079092b0cb98c1f008f38ea)), closes [#619](https://github.com/terraform-routeros/terraform-provider-routeros/issues/619) ## [1.72.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.71.0...v1.72.0) (2024-12-22) ### Features * **ipv6:** new resource routeros_ipv6_firewall_mangle ([e6a0c1e](https://github.com/terraform-routeros/terraform-provider-routeros/commit/e6a0c1e663f6a6e0ed447e6d2d46167562afe9a0)) * **ipv6:** new resource routeros_ipv6_firewall_nat ([03c2587](https://github.com/terraform-routeros/terraform-provider-routeros/commit/03c2587c8be7583ff122fafeb96edfe6be489d35)) * **pppoe:** Add new resource `routeros_interface_pppoe_server` ([f04afd1](https://github.com/terraform-routeros/terraform-provider-routeros/commit/f04afd1993c22a0010f60d2cd1dbd7150d854f84)), closes [#617](https://github.com/terraform-routeros/terraform-provider-routeros/issues/617) ### Bug Fixes * **ipv6_dhcp_server_option:** Add Hotspot 2.0 fields ([1d64d8c](https://github.com/terraform-routeros/terraform-provider-routeros/commit/1d64d8c04adf9204bb554da2ec8bec9521203db0)), closes [#605](https://github.com/terraform-routeros/terraform-provider-routeros/issues/605) ## [1.71.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.70.0...v1.71.0) (2024-12-02) ### Features * Add interface bridge filter ([#608](https://github.com/terraform-routeros/terraform-provider-routeros/issues/608)) ([441f11c](https://github.com/terraform-routeros/terraform-provider-routeros/commit/441f11cc21f0637096f68bc1c22c90afb01dfb08)) ### Bug Fixes * routeros_ipv6_dhcp_client prefix_hint validation issue ([#611](https://github.com/terraform-routeros/terraform-provider-routeros/issues/611)) ([fb51382](https://github.com/terraform-routeros/terraform-provider-routeros/commit/fb51382092de81ad6846c980403a314cc479fbf7)), closes [#609](https://github.com/terraform-routeros/terraform-provider-routeros/issues/609) ## [1.70.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.69.2...v1.70.0) (2024-11-24) ### Features * **wave2:** Add new resource `routeros_interface_wireless_connect_list` ([10078df](https://github.com/terraform-routeros/terraform-provider-routeros/commit/10078df6c341d20e2d7e94344bafbff224312828)), closes [#605](https://github.com/terraform-routeros/terraform-provider-routeros/issues/605) ## [1.69.2](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.69.1...v1.69.2) (2024-11-21) ### Bug Fixes * **ds:** warning Field 'revision' not found in the schema in routeros_system_routerboard datasource ([1b6f86b](https://github.com/terraform-routeros/terraform-provider-routeros/commit/1b6f86b28b5d566dbda32e6f0ed3cf95cacd251c)), closes [#602](https://github.com/terraform-routeros/terraform-provider-routeros/issues/602) ## [1.69.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.69.0...v1.69.1) (2024-11-18) ### Bug Fixes * **ospf:** Change the “network” attribute ([f057f18](https://github.com/terraform-routeros/terraform-provider-routeros/commit/f057f184608e9de9c08e2f5b773e0ed4f4f740af)), closes [#598](https://github.com/terraform-routeros/terraform-provider-routeros/issues/598) ## [1.69.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.68.0...v1.69.0) (2024-11-14) ### Features * **ipv6:** Add new resource `routeros_ipv6_settings` ([4410ddc](https://github.com/terraform-routeros/terraform-provider-routeros/commit/4410ddc9941257e9ff58b40d434d7b59b6ea886b)), closes [#596](https://github.com/terraform-routeros/terraform-provider-routeros/issues/596) ## [1.68.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.67.0...v1.68.0) (2024-11-13) ### Features * **ipv6:** Add new resource `routeros_interface_6to4` ([742e6bd](https://github.com/terraform-routeros/terraform-provider-routeros/commit/742e6bd55fd4682e952bd35a7acece9968951978)), closes [#593](https://github.com/terraform-routeros/terraform-provider-routeros/issues/593) ### Bug Fixes * **helpers:** Fix PropKeepaliveRw ([32ee5bc](https://github.com/terraform-routeros/terraform-provider-routeros/commit/32ee5bceffdd4c4ed66461ddfc7e469b95068de8)) * **helpers:** Fix the plan was not empty ([31d420f](https://github.com/terraform-routeros/terraform-provider-routeros/commit/31d420f4f60998830bc860ba1e0f38025cd241d1)) * **mangle-connection-state:** typo in `established` value ([#595](https://github.com/terraform-routeros/terraform-provider-routeros/issues/595)) ([19c6c97](https://github.com/terraform-routeros/terraform-provider-routeros/commit/19c6c974a9f03f3848e5662ccb73401a72d0f4cf)) ## [1.67.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.66.0...v1.67.0) (2024-11-10) ### Features * **ds:** Add new datasource `routeros_system_routerboard` ([2f78e94](https://github.com/terraform-routeros/terraform-provider-routeros/commit/2f78e94ceaf4c484376a5909159675371f3da90b)), closes [#588](https://github.com/terraform-routeros/terraform-provider-routeros/issues/588) * **vxlan:** Add new resource `routeros_interface_vxlan_vteps` ([1e72222](https://github.com/terraform-routeros/terraform-provider-routeros/commit/1e72222b890de8ef8a72d2adad7d46138835a0c8)), closes [#590](https://github.com/terraform-routeros/terraform-provider-routeros/issues/590) * **vxlan:** Add new resource `routeros_interface_vxlan` ([7ccb49f](https://github.com/terraform-routeros/terraform-provider-routeros/commit/7ccb49f5418b545afda724f933993e94f134324b)) ## [1.66.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.65.1...v1.66.0) (2024-10-11) ### Features * **ipv6:** Add new resource `routeros_ipv6_dhcp_server_option` ([c224bb1](https://github.com/terraform-routeros/terraform-provider-routeros/commit/c224bb192b090ac553cf67aade61e81aab9db93a)) * **ipv6:** Add new resource `routeros_ipv6_dhcp_server` ([23ec941](https://github.com/terraform-routeros/terraform-provider-routeros/commit/23ec9414b9c17a6acf483a602ad741135104b012)) * **ipv6:** Add new resource `routeros_ipv6_pool` ([96d1c48](https://github.com/terraform-routeros/terraform-provider-routeros/commit/96d1c48ed30efe0ff0d36e716bbac84282431e33)) * **ipv6:** New resource `routeros_ipv6_dhcp_server_option_sets` ([b92458b](https://github.com/terraform-routeros/terraform-provider-routeros/commit/b92458bf79cc9e987beb137df85e40d2b76c436b)) ## [1.65.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.65.0...v1.65.1) (2024-10-09) ### Bug Fixes * **ipsec:** Fix `auth_method` attribute validation ([#582](https://github.com/terraform-routeros/terraform-provider-routeros/issues/582)) ([98516b0](https://github.com/terraform-routeros/terraform-provider-routeros/commit/98516b07f1dbf99b9fa444f95b4a04ed61b5dcec)), closes [#581](https://github.com/terraform-routeros/terraform-provider-routeros/issues/581) ## [1.65.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.64.2...v1.65.0) (2024-10-07) ### Features * Disable warning output on system resources ([c799f29](https://github.com/terraform-routeros/terraform-provider-routeros/commit/c799f299629fd82c3004dd1903487d694aeffbbf)) * **ipsec:** Add new resource `routeros_ip_ipsec_identity` ([afdbadb](https://github.com/terraform-routeros/terraform-provider-routeros/commit/afdbadb7297560dc97d07cbd0abe8d1f7fa0fb9f)) * **ipsec:** Add new resource `routeros_ip_ipsec_key` ([14865b9](https://github.com/terraform-routeros/terraform-provider-routeros/commit/14865b9fa989e79a07a9e46857399cf560208f61)) * **ipsec:** Add new resource `routeros_ip_ipsec_mode_config` ([ca88a77](https://github.com/terraform-routeros/terraform-provider-routeros/commit/ca88a77f14bd5177938d20930da4ccd515e647fa)) * **ipsec:** Add new resource `routeros_ip_ipsec_policy_group` ([d4c0817](https://github.com/terraform-routeros/terraform-provider-routeros/commit/d4c0817f6af31a3b5d1fa766bf9c4c415b43aefd)) * **ipsec:** Add new resource `routeros_ip_ipsec_policy` ([9ba2bf9](https://github.com/terraform-routeros/terraform-provider-routeros/commit/9ba2bf961a0d7dafaf5d778a10f0a2e153f0d666)) * **ipsec:** Add new resource `routeros_ip_ipsec_profile` ([66aa2f8](https://github.com/terraform-routeros/terraform-provider-routeros/commit/66aa2f8c830a4cfe925a315c36114ded9842264d)) * **ipsec:** Add new resource `routeros_ip_ipsec_proposal` ([9fee803](https://github.com/terraform-routeros/terraform-provider-routeros/commit/9fee803c5a19ad244d63ff8f052dc6c9de90c47f)) * **ipsec:** Add new resource `routeros_ip_ipsec_settings` ([7388cae](https://github.com/terraform-routeros/terraform-provider-routeros/commit/7388cae3a5753ef9c0dfbf5c2423497ee9eba838)) * **ipsec:** New resource `routeros_ip_ipsec_peer` ([7600d45](https://github.com/terraform-routeros/terraform-provider-routeros/commit/7600d457e5e4245c9c743f2f28d42194c2a0f0e8)) ### Bug Fixes * Adding fields to skip for rx and tx on resource_interface_ethernet.go ([#573](https://github.com/terraform-routeros/terraform-provider-routeros/issues/573)) ([1a905f5](https://github.com/terraform-routeros/terraform-provider-routeros/commit/1a905f5e26afbd782bbd9f67c18e49abe6937ea1)) * **file:** Field 'last_modified' not found in the schema ([#580](https://github.com/terraform-routeros/terraform-provider-routeros/issues/580)) ([cb4635a](https://github.com/terraform-routeros/terraform-provider-routeros/commit/cb4635a97b50b1e33c82622ee3f3a3defe1e6b69)), closes [#579](https://github.com/terraform-routeros/terraform-provider-routeros/issues/579) * **ipsec:** Add the lost attributes ([6f61879](https://github.com/terraform-routeros/terraform-provider-routeros/commit/6f61879ef8e84254fbf0a602ec8a09b714bc58be)) * validation for routeros_wifi_security.wps ([#578](https://github.com/terraform-routeros/terraform-provider-routeros/issues/578)) ([770bfe3](https://github.com/terraform-routeros/terraform-provider-routeros/commit/770bfe37c5708227daa3a891581d86f09c9c4b54)) * **wireless:** Delete required parameters ([616049e](https://github.com/terraform-routeros/terraform-provider-routeros/commit/616049e04bd351111c7515e801b3e9e229dc3da8)) * **wireless:** Delete required parameters ([33793fd](https://github.com/terraform-routeros/terraform-provider-routeros/commit/33793fd70aef7580862b577cd6f001cf9d954306)) ## [1.64.2](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.64.1...v1.64.2) (2024-09-29) ### Bug Fixes * Unable to remove routeros_interface_wireless from config ([#571](https://github.com/terraform-routeros/terraform-provider-routeros/issues/571)) ([8990eb0](https://github.com/terraform-routeros/terraform-provider-routeros/commit/8990eb05c63859b0d58be996d7d2a886868ab13a)), closes [#570](https://github.com/terraform-routeros/terraform-provider-routeros/issues/570) ## [1.64.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.64.0...v1.64.1) (2024-09-28) ### Bug Fixes * **no-release:** Update resource_ipv6_address.go ([#568](https://github.com/terraform-routeros/terraform-provider-routeros/issues/568)) ([587f527](https://github.com/terraform-routeros/terraform-provider-routeros/commit/587f527c532ffabfe404508512ab5b9185f68695)) * Update resource_ip_address.go ([#569](https://github.com/terraform-routeros/terraform-provider-routeros/issues/569)) ([25a6496](https://github.com/terraform-routeros/terraform-provider-routeros/commit/25a64968c0bb8279f599859af371309fcbca1871)) ## [1.64.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.63.1...v1.64.0) (2024-09-27) ### Features * **wireless:** Add new resource `routeros_interface_wireless_access_list` ([cd82592](https://github.com/terraform-routeros/terraform-provider-routeros/commit/cd825923390b489e41463b71e6ea253286f28c25)) * **wireless:** Add new resource `routeros_interface_wireless_security_profiles` ([ef40fd2](https://github.com/terraform-routeros/terraform-provider-routeros/commit/ef40fd25f448c3ae1d92d2c2a60fcdde782f5df4)) * **wireless:** Add new resource `routeros_interface_wireless` ([15c2650](https://github.com/terraform-routeros/terraform-provider-routeros/commit/15c265088c024a5b770fda1990ea3713c724d253)) ### Bug Fixes * **serialize:** Fix `PropTransformSet` ([8baebae](https://github.com/terraform-routeros/terraform-provider-routeros/commit/8baebae070e047a3dad3f86aa72cf502f053d781)) * **serialize:** Fix the transformation of attribute names. ([9796820](https://github.com/terraform-routeros/terraform-provider-routeros/commit/9796820aa413ad6f45731b613a1cfa88fc124e0a)) * **tool_sniffer:** Add resource state control ([eb53e45](https://github.com/terraform-routeros/terraform-provider-routeros/commit/eb53e456ffce679464bb9023df59056663b9c6e9)) * **wireless_security_profile:** Add `Sensitive` flag to attributes ([909b4c7](https://github.com/terraform-routeros/terraform-provider-routeros/commit/909b4c792e169eec5c60619156be8bd10dfab7df)) * **wireless:** Add missing field ([c112740](https://github.com/terraform-routeros/terraform-provider-routeros/commit/c112740a660362528f9d53037e97e59c7138441f)) ## [1.63.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.63.0...v1.63.1) (2024-09-26) ### Features * **no-release:** Add QR code generation for WiFi ([#564](https://github.com/terraform-routeros/terraform-provider-routeros/issues/564)) ([9c39ae2](https://github.com/terraform-routeros/terraform-provider-routeros/commit/9c39ae2b52e62dfc7b3451887ee726a137b1c64c)) ### Bug Fixes * Missing fields in `routeros_system_user ` (introduced in 7.16) ([3ae2e10](https://github.com/terraform-routeros/terraform-provider-routeros/commit/3ae2e1030a7e30e935da4cdc316028c2abd3c91b)), closes [#560](https://github.com/terraform-routeros/terraform-provider-routeros/issues/560) * **no-release:** Add field introduced in 7.16 `routeros_ip_address` ([de72d8e](https://github.com/terraform-routeros/terraform-provider-routeros/commit/de72d8e6c421dc4c4dee36c0a45d207a8d232c9c)) * **no-release:** Update `datasource_ipv6_addresses.go` ROS 7.16 ([0c0306b](https://github.com/terraform-routeros/terraform-provider-routeros/commit/0c0306b975c6be0314ea773b6ccc6f4acbfbe04d)) * **no-release:** Update `resource_system_logging.go` ROS 7.16 ([884addc](https://github.com/terraform-routeros/terraform-provider-routeros/commit/884addca655d60e91b74bd9cc507bb17cc55e6ca)) ## [1.63.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.62.0...v1.63.0) (2024-09-24) ### Features * Add new resource `routeros_tool_sniffer` ([f046966](https://github.com/terraform-routeros/terraform-provider-routeros/commit/f0469663fe7ea395825dc92c6a246e09dcd0f81c)) * **hotspot:** Add new resource `routeros_ip_hotspot_ip_binding` ([f2e27b4](https://github.com/terraform-routeros/terraform-provider-routeros/commit/f2e27b4732f863400c6f6c7f8341315019623f47)) * **hotspot:** Add new resource `routeros_ip_hotspot_profile` ([06b974b](https://github.com/terraform-routeros/terraform-provider-routeros/commit/06b974bdfbd6508a3f32a6b52456f0c8a6ba10b0)) * **hotspot:** Add new resource `routeros_ip_hotspot_service_port` ([153bf68](https://github.com/terraform-routeros/terraform-provider-routeros/commit/153bf68723a1def279431593ed5f5cc2bf2f1ddc)) * **hotspot:** Add new resource `routeros_ip_hotspot_user_profile` ([4de2db9](https://github.com/terraform-routeros/terraform-provider-routeros/commit/4de2db943c82de89e2081b8b32ffed8621f42ab9)) * **hotspot:** Add new resource `routeros_ip_hotspot_user` ([b897532](https://github.com/terraform-routeros/terraform-provider-routeros/commit/b897532232c12245b4c92f27ca129ebe8f6c8d31)) * **hotspot:** Add new resource `routeros_ip_hotspot_walled_garden_ip` ([92778ff](https://github.com/terraform-routeros/terraform-provider-routeros/commit/92778ff184124ce8d2d7dc0dfc266b29b6f306ef)) * **hotspot:** Add new resource `routeros_ip_hotspot_walled_garden` ([9c111ee](https://github.com/terraform-routeros/terraform-provider-routeros/commit/9c111eeefa0ce90e0bc2f02c9c2d9a4794487cbe)) * **hotspot:** Add new resource `routeros_ip_hotspot` ([1da8f3d](https://github.com/terraform-routeros/terraform-provider-routeros/commit/1da8f3d755ec0a35836b67037d57e40d27d3453f)) ### Bug Fixes * **dns_adlist:** Change an invalid resource name. ([e77bcbf](https://github.com/terraform-routeros/terraform-provider-routeros/commit/e77bcbf6f3bccfc3271269a4ad1fbff00038cfaf)), closes [#554](https://github.com/terraform-routeros/terraform-provider-routeros/issues/554) ## [1.62.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.61.2...v1.62.0) (2024-09-19) ### Features * **resource_dns_adlist:** Add dns ad list support ([b1ca164](https://github.com/terraform-routeros/terraform-provider-routeros/commit/b1ca164c8e1b2b5f6e9ecca90abe62f84c5a488c)), closes [#554](https://github.com/terraform-routeros/terraform-provider-routeros/issues/554) ## [1.61.2](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.61.1...v1.61.2) (2024-09-02) ### Bug Fixes * Add a validator for map name keys. ([059de30](https://github.com/terraform-routeros/terraform-provider-routeros/commit/059de305bcc829fb9be16c49c9903049f074cb13)), closes [#552](https://github.com/terraform-routeros/terraform-provider-routeros/issues/552) * **data.routeros_interfaces:** Fix the schema ([f9489a2](https://github.com/terraform-routeros/terraform-provider-routeros/commit/f9489a23148c4bb96a1b8db76cc409a4a9bda00a)), closes [#550](https://github.com/terraform-routeros/terraform-provider-routeros/issues/550) * **routeros_capsman_provisioning :** Change attributes type ([b9967d9](https://github.com/terraform-routeros/terraform-provider-routeros/commit/b9967d9c65f20da871cd6bc60597b49bd9d99d14)), closes [#551](https://github.com/terraform-routeros/terraform-provider-routeros/issues/551) [#551](https://github.com/terraform-routeros/terraform-provider-routeros/issues/551) * **system_user_group:** Fix `policy` validator ([6a0a0bd](https://github.com/terraform-routeros/terraform-provider-routeros/commit/6a0a0bdd370680b0ac19172ff40bc555a08c82cc)) ## [1.61.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.61.0...v1.61.1) (2024-08-27) ### Bug Fixes * **routeros_system_user_group:** Fix change detection in sets with `SetUnset` values ([#547](https://github.com/terraform-routeros/terraform-provider-routeros/issues/547)) ([d51c4c2](https://github.com/terraform-routeros/terraform-provider-routeros/commit/d51c4c207e9f5df3979c0b090a34f24ad713c062)), closes [#544](https://github.com/terraform-routeros/terraform-provider-routeros/issues/544) ## [1.61.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.60.0...v1.61.0) (2024-08-23) ### Features * Add `class_id` property support to the `routeros_ip_dhcp_server_lease` resource ([90033af](https://github.com/terraform-routeros/terraform-provider-routeros/commit/90033afb96247b4f9f70ea5974259cdcc16d9a08)) * Add `discover_interval` property support to the `routeros_ip_neighbor_discovery_settings` resource ([8233ace](https://github.com/terraform-routeros/terraform-provider-routeros/commit/8233ace372ea9dd9bbb0b9ad83dfbaf58c27a912)) * Add `forward_reserved_addresses` property support to the `routeros_interface_bridge` resource ([600e2af](https://github.com/terraform-routeros/terraform-provider-routeros/commit/600e2af88f0a8e6ed9c4fe0e8e3c0a73a7359f0b)) * Add `lldp_vlan_info` property support to the `routeros_ip_neighbor_discovery_settings` resource ([c2b07af](https://github.com/terraform-routeros/terraform-provider-routeros/commit/c2b07aff724758326f5551aa1d7efa87b72d16fa)) * Add `max_learned_entries` property support to the `routeros_interface_bridge` resource ([a8b4191](https://github.com/terraform-routeros/terraform-provider-routeros/commit/a8b4191e67ad2e399f18729352244898caebdbf3)) * Add `max_sessions` property support to the `routeros_ip_service` resource ([9007755](https://github.com/terraform-routeros/terraform-provider-routeros/commit/9007755b50236cb8676e191c9af4e6db8629f976)) * Add `mdns_repeat_ifaces` property support to the `routeros_ip_dns` resource ([fb71ff7](https://github.com/terraform-routeros/terraform-provider-routeros/commit/fb71ff71ad7392088f4ee21434e9b0fc833ed252)) * Add `slave_name_format` property support to the `routeros_wifi_provisioning` resource ([2ad0de6](https://github.com/terraform-routeros/terraform-provider-routeros/commit/2ad0de6ac0e10f431000022badec0c6595c8938c)) * Add the `comment` property support to the DHCP resources ([0d326e9](https://github.com/terraform-routeros/terraform-provider-routeros/commit/0d326e93ea28e4f7bf2d71a0399a116d759033e0)) ### Bug Fixes * Remove the default value for the `radio_mac` property in the `routeros_wifi_provisioning` resource ([a35d307](https://github.com/terraform-routeros/terraform-provider-routeros/commit/a35d307c659d973c5a7ab0e48f70a38d0e70d17e)) * **routeros_system_resource:** Add `bad_blocks` attribute to the ignored ones ([7b03141](https://github.com/terraform-routeros/terraform-provider-routeros/commit/7b03141f5fbbcbf56301149610dea93d1debdd82)), closes [#541](https://github.com/terraform-routeros/terraform-provider-routeros/issues/541) * supress hw offload writable ([6c358eb](https://github.com/terraform-routeros/terraform-provider-routeros/commit/6c358eba75de36baf5653f79da653cd10ab26982)), closes [#540](https://github.com/terraform-routeros/terraform-provider-routeros/issues/540) ## [1.60.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.59.4...v1.60.0) (2024-08-19) ### Features * Add the `routeros_system_led_settings` resource to manage the LED settings ([82434f8](https://github.com/terraform-routeros/terraform-provider-routeros/commit/82434f8c3d7f266dbd6624860d23019f29907975)) * Add the `routeros_system_led` resource to manage LEDs ([5131156](https://github.com/terraform-routeros/terraform-provider-routeros/commit/5131156a03608b531813cb7f340a1e5201d56187)) * Add the `routeros_system_routerboard_button_mode` resource to manage the mode button ([243dd9d](https://github.com/terraform-routeros/terraform-provider-routeros/commit/243dd9d0686643b1e02ba15699897228cc08fa7f)) * Add the `routeros_system_routerboard_button_reset` resource to manage the reset button ([358f640](https://github.com/terraform-routeros/terraform-provider-routeros/commit/358f640dd1f246c8a67af35fc5f1bdd01a8d7230)) * Add the `routeros_system_routerboard_button_wps` resource to manage the WPS button ([85db3b5](https://github.com/terraform-routeros/terraform-provider-routeros/commit/85db3b566a8641ac0256344e554a79ddb2fb39d0)) * Add the `routeros_system_routerboard_settings` resource to manage the RouterBOARD settings ([39a7e39](https://github.com/terraform-routeros/terraform-provider-routeros/commit/39a7e3957b46e47e4fd0c83da252eb7039c84f34)) * Add the `routeros_system_routerboard_usb` resource to manage the USB port ([4251b6a](https://github.com/terraform-routeros/terraform-provider-routeros/commit/4251b6a6552bd8a07edebab046f5f97a0d85300a)) ## [1.59.4](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.59.3...v1.59.4) (2024-08-13) ### Bug Fixes * **datasources:** Datasource int overflow ([#507](https://github.com/terraform-routeros/terraform-provider-routeros/issues/507)) ([aea6028](https://github.com/terraform-routeros/terraform-provider-routeros/commit/aea60289833ae74e15740447d36020d4485f72b2)) ## [1.59.3](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.59.2...v1.59.3) (2024-08-12) ### Bug Fixes * Add missing attributes, add fields to ignore. Fix wrong `new_dst_ports` type ([c570002](https://github.com/terraform-routeros/terraform-provider-routeros/commit/c570002b40171054b85b62a1e59b2dc907791100)), closes [#538](https://github.com/terraform-routeros/terraform-provider-routeros/issues/538) * **netwatch:** Add `http_codes` attribute to skip fields. ([e7d0356](https://github.com/terraform-routeros/terraform-provider-routeros/commit/e7d03563143c4a70e7052cb8652513f375e4bfb3)) ## [1.59.2](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.59.1...v1.59.2) (2024-08-07) ### Bug Fixes * Can not set rtt parameters in `routeros_tool_netwatch` ([afa971e](https://github.com/terraform-routeros/terraform-provider-routeros/commit/afa971e337f6141c4dfa717c791807a52ffc0692)), closes [#535](https://github.com/terraform-routeros/terraform-provider-routeros/issues/535) ## [1.59.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.59.0...v1.59.1) (2024-08-07) ### Bug Fixes * **bridge_port:** Fix the `priority` attribute type. ([4f342fb](https://github.com/terraform-routeros/terraform-provider-routeros/commit/4f342fb0fcedce97f0dbff806f0c60a6ed072b8b)), closes [#528](https://github.com/terraform-routeros/terraform-provider-routeros/issues/528) * **firewall_raw:** "no track" action in routeros_ip_firewall_raw needs tweaking ([9d46a55](https://github.com/terraform-routeros/terraform-provider-routeros/commit/9d46a5503163b60534d63f414ddfd1b1a50ea631)), closes [#529](https://github.com/terraform-routeros/terraform-provider-routeros/issues/529) * **wireguard_peer:** Need new filed 'is-responder' in resource 'routeros_interface_wireguard_peer' ([a31a394](https://github.com/terraform-routeros/terraform-provider-routeros/commit/a31a394e3abd8bcab3af06d6ab35d4b4f0abc89a)), closes [#530](https://github.com/terraform-routeros/terraform-provider-routeros/issues/530) ## [1.59.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.58.2...v1.59.0) (2024-08-05) ### Features * Add `routeros_routing_rule` resource ([a263193](https://github.com/terraform-routeros/terraform-provider-routeros/commit/a26319308ae96b41fd0ffe291fb62073459378f7)), closes [#524](https://github.com/terraform-routeros/terraform-provider-routeros/issues/524) * Add `routeros_tool_netwatch` resource ([2150241](https://github.com/terraform-routeros/terraform-provider-routeros/commit/215024137033233ab13a22ae731ef8a72f4a3612)), closes [#487](https://github.com/terraform-routeros/terraform-provider-routeros/issues/487) ## [1.58.2](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.58.1...v1.58.2) (2024-08-04) ### Bug Fixes * **interface_lte:** Add missing attributes ([f67e8d9](https://github.com/terraform-routeros/terraform-provider-routeros/commit/f67e8d90242b0b1e0f9c630cf0ffd37a819bd049)), closes [#522](https://github.com/terraform-routeros/terraform-provider-routeros/issues/522) * **ip_firewall:** Deleting routeros_ip_firewall_filter.in_interface tries to PATCH ([2189054](https://github.com/terraform-routeros/terraform-provider-routeros/commit/21890540901501e7f8da61eb23a452ad52791f44)), closes [#521](https://github.com/terraform-routeros/terraform-provider-routeros/issues/521) * **ipv4_nat:** Need new filed 'randomise_ports' in action 'endpoint-independent-nat' ([51e161d](https://github.com/terraform-routeros/terraform-provider-routeros/commit/51e161d5b090762fed7bd6f638d36f9d31564ddc)), closes [#520](https://github.com/terraform-routeros/terraform-provider-routeros/issues/520) ## [1.58.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.58.0...v1.58.1) (2024-08-02) ### Bug Fixes * resource_system_logging missing topics and excluding Fixes [#518](https://github.com/terraform-routeros/terraform-provider-routeros/issues/518) ([2805dc3](https://github.com/terraform-routeros/terraform-provider-routeros/commit/2805dc35453f8b8051bb9b612814d5fef34a31df)) * routeros_system_logging_action and default settings ([1a5d02c](https://github.com/terraform-routeros/terraform-provider-routeros/commit/1a5d02ce5a8b27bdb97ba38f241db24627621882)), closes [#517](https://github.com/terraform-routeros/terraform-provider-routeros/issues/517) ## [1.58.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.57.2...v1.58.0) (2024-07-31) ### Features * **lte:** Add LTE resources ([fcaf349](https://github.com/terraform-routeros/terraform-provider-routeros/commit/fcaf3495a50f3cabed77c240de9604d4d5347b3e)), closes [#464](https://github.com/terraform-routeros/terraform-provider-routeros/issues/464) ### Bug Fixes * **ethernet:** Schema Error on SFP Ports ([01e4c11](https://github.com/terraform-routeros/terraform-provider-routeros/commit/01e4c119d86ade3cfaf040e3deb68749f14282b5)), closes [#514](https://github.com/terraform-routeros/terraform-provider-routeros/issues/514) * **firewall_nat:** add NAT firewall action "endpoint-independent-nat" ([d55b30d](https://github.com/terraform-routeros/terraform-provider-routeros/commit/d55b30d07ef8f9c2d7c3b53088c8224e819907d9)), closes [#516](https://github.com/terraform-routeros/terraform-provider-routeros/issues/516) * **system_logging:** Bugfix in routeros_system_logging ([aa1d376](https://github.com/terraform-routeros/terraform-provider-routeros/commit/aa1d3762000006aac00d13a7ae74ee5330c0245f)), closes [#515](https://github.com/terraform-routeros/terraform-provider-routeros/issues/515) ## [1.57.2](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.57.1...v1.57.2) (2024-07-30) ### Bug Fixes * **certificate:** Certificate import ambiguous value of file-name ([01f4294](https://github.com/terraform-routeros/terraform-provider-routeros/commit/01f4294a2898ecdceb4772db048dd620fa9a221e)), closes [#511](https://github.com/terraform-routeros/terraform-provider-routeros/issues/511) ## [1.57.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.57.0...v1.57.1) (2024-07-30) ### Bug Fixes * **ospf_interface_template:** RouterOS 7.x & OSPF Interface Template Auth Key ([995ba46](https://github.com/terraform-routeros/terraform-provider-routeros/commit/995ba46e8d8f8fc11c9d47a300f36879876c285f)), closes [#510](https://github.com/terraform-routeros/terraform-provider-routeros/issues/510) ## [1.57.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.56.0...v1.57.0) (2024-07-23) ### Features * Update the `routeros_ovpn_server` resource to support multiple values ([668ef09](https://github.com/terraform-routeros/terraform-provider-routeros/commit/668ef0986404bea864150b64509b86f43e5b4494)) ### Bug Fixes * **ipv6_neighbor_discovery:** Change the schema attributes ([9688bab](https://github.com/terraform-routeros/terraform-provider-routeros/commit/9688babe485e51b2acc981b6e8a8bb8edd26e98a)), closes [#509](https://github.com/terraform-routeros/terraform-provider-routeros/issues/509) * Update the `routeros_ovpn_server` resource to handle default values correctly ([f377a26](https://github.com/terraform-routeros/terraform-provider-routeros/commit/f377a2615219baed70ffc779f9be0fe022e42712)) ## [1.56.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.55.0...v1.56.0) (2024-07-04) ### Features * Add `enable_ipv6_accounting` introduced in 7.15 to the `routeros_ppp_aaa` resource ([4c792c7](https://github.com/terraform-routeros/terraform-provider-routeros/commit/4c792c74a4d5e5de43fc5c62176ea6f74925336c)) * Add `require_message_auth` introduced in 7.15 to the `routeros_radius` resource ([6946d95](https://github.com/terraform-routeros/terraform-provider-routeros/commit/6946d95654b504dbc214d046dbab0723dbdb4e0d)) * Add `require_message_auth` introduced in 7.15 to the `routeros_user_manager_settings` resource ([91b41b7](https://github.com/terraform-routeros/terraform-provider-routeros/commit/91b41b7b3c184ec1f0c2cede4e61fadc437171a2)) * Add `reselect_interval` introduced in 7.15 to the `routeros_wifi_channel` resource ([77186c0](https://github.com/terraform-routeros/terraform-provider-routeros/commit/77186c0fbc478c2e8c4a06bc1b5e330e5db402ac)) * Add `sfp_ignore_rx_los` introduced in 7.15 to the `routeros_interface_ethernet` resource ([a361715](https://github.com/terraform-routeros/terraform-provider-routeros/commit/a36171577d57a5af9050cf8e57639f962c5f4ae8)) * Add current CAPsMAN identity computed properties introduced in 7.15 to the `routeros_wifi_cap` resource ([f0fae7b](https://github.com/terraform-routeros/terraform-provider-routeros/commit/f0fae7baf1771b6f65665269794bc68a25b40e23)) ### Bug Fixes * Fix the `AlwaysPresentNotUserProvided` helper to ignore stored empty values as RouterOS started returning such in 7.15 ([c22e017](https://github.com/terraform-routeros/terraform-provider-routeros/commit/c22e017534a6024a35d530d8da689a69ce07f360)) ## [1.55.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.54.3...v1.55.0) (2024-07-02) ### Features * Add state migrator helper to convert scalar values to lists ([858ecab](https://github.com/terraform-routeros/terraform-provider-routeros/commit/858ecab2b52c8933051c2ae992d272969b90c6be)) * Update properties in `routeros_interface_dot1x_client` and `routeros_interface_dot1x_server` to support multiple values ([66ab1d2](https://github.com/terraform-routeros/terraform-provider-routeros/commit/66ab1d2cec80d8c677190315d258b47ca9fe50fa)) * Update properties in `routeros_ip_dhcp_server_network` to support multiple values ([bc1cf68](https://github.com/terraform-routeros/terraform-provider-routeros/commit/bc1cf68b5f3126f0f19bd3f4b4288cc269f25f67)) * Update the `servers` property in `routeros_dns` to support multiple values ([ea25f00](https://github.com/terraform-routeros/terraform-provider-routeros/commit/ea25f0088d2da7dfaa001fab0b06c0bc67bbc847)) * Update the `service` property in `routeros_radius` to support multiple values ([a1becca](https://github.com/terraform-routeros/terraform-provider-routeros/commit/a1beccacd612960abb27b4c66c4941e0e3f0cf4c)) * Update the `vlan_ids` property in `routeros_interface_bridge_vlan` to support multiple values ([3ec3d34](https://github.com/terraform-routeros/terraform-provider-routeros/commit/3ec3d343d9ed0bf501624fae89aae5840da8fa8e)) * Update the frequency properties in `routeros_capsman_channel` to support multiple values ([14a6e4e](https://github.com/terraform-routeros/terraform-provider-routeros/commit/14a6e4e45a91be4f4811f97a7696c25460e1313b)) ### Bug Fixes * Add state migrator to `routeros_snmp_community` to fix backward compatibility ([27c8588](https://github.com/terraform-routeros/terraform-provider-routeros/commit/27c858806bdf9f79bf96a4cd5e85746abc2b2e99)) * Fix `tagged` and `untagged` properties in `routeros_interface_bridge_vlan` to ignore values order ([eb8ff28](https://github.com/terraform-routeros/terraform-provider-routeros/commit/eb8ff28b6c125f20c45ebb783ed248a28f72b935)) * Fix `topics` property type in `routeros_system_logging` to ignore values order ([0e3c29f](https://github.com/terraform-routeros/terraform-provider-routeros/commit/0e3c29f62f587db7b340fee8299c6ec0a3622898)) ## [1.54.3](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.54.2...v1.54.3) (2024-06-27) ### Features * **no-release:** Added ipv6 filter data ([#496](https://github.com/terraform-routeros/terraform-provider-routeros/issues/496)) ([6d45e88](https://github.com/terraform-routeros/terraform-provider-routeros/commit/6d45e88d0af4e99fd552754835d865a034bb54ac)) * **no-release:** Support Multiple VLAN Registration protocol (MVRP). ([#497](https://github.com/terraform-routeros/terraform-provider-routeros/issues/497)) ([0dc994a](https://github.com/terraform-routeros/terraform-provider-routeros/commit/0dc994aed90ed6453dbe422b0ede093b1fae08c7)), closes [#492](https://github.com/terraform-routeros/terraform-provider-routeros/issues/492) [#493](https://github.com/terraform-routeros/terraform-provider-routeros/issues/493) ### Bug Fixes * Field 'vrf' not found in the schema (introduced in 7.15) ([563401b](https://github.com/terraform-routeros/terraform-provider-routeros/commit/563401bc8c8b8b980afdbd2ad36c424ad8134ebb)), closes [#490](https://github.com/terraform-routeros/terraform-provider-routeros/issues/490) * **no-release:** Allow a set of `addresses` ([#498](https://github.com/terraform-routeros/terraform-provider-routeros/issues/498)) ([bcf417f](https://github.com/terraform-routeros/terraform-provider-routeros/commit/bcf417f491090bad60cb4f2a9fe313c146259d19)), closes [#495](https://github.com/terraform-routeros/terraform-provider-routeros/issues/495) * **no-release:** nil resources ([#486](https://github.com/terraform-routeros/terraform-provider-routeros/issues/486)) ([8571dea](https://github.com/terraform-routeros/terraform-provider-routeros/commit/8571dea493a5a22d167c083e65e744f97a50c05b)) * **routeros_interface_wireguard_peer:** Field 'name' not found in the schema (introduced in 7.15) ([9fb13ad](https://github.com/terraform-routeros/terraform-provider-routeros/commit/9fb13ad7523be815ae41cd5b35c5d7e889e02a9e)), closes [#494](https://github.com/terraform-routeros/terraform-provider-routeros/issues/494) * **routeros_ip_neighbor_discovery_settings:** Multiple fields not found in schema (introduced in 7.15) ([7f44443](https://github.com/terraform-routeros/terraform-provider-routeros/commit/7f44443784cfe5b4372c7af7aee1da94acc0d1c1)), closes [#491](https://github.com/terraform-routeros/terraform-provider-routeros/issues/491) ## [1.54.2](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.54.1...v1.54.2) (2024-06-04) ### Bug Fixes * **fw-mangle:** Fix `dst_address_list` validation ([0eb25c9](https://github.com/terraform-routeros/terraform-provider-routeros/commit/0eb25c973c2022704a1f85670f125fb5c772388b)), closes [#480](https://github.com/terraform-routeros/terraform-provider-routeros/issues/480) ## [1.54.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.54.0...v1.54.1) (2024-05-30) ### Bug Fixes * **vrrp:** Add `group-authority` attribute handling to the `group-master` replacement ([58cf139](https://github.com/terraform-routeros/terraform-provider-routeros/commit/58cf139015490181973170d97d1e3931919b1af0)), closes [#446](https://github.com/terraform-routeros/terraform-provider-routeros/issues/446) ## [1.54.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.53.0...v1.54.0) (2024-05-29) ### Features * Add bandwidth server resource ([#475](https://github.com/terraform-routeros/terraform-provider-routeros/issues/475)) ([d98ce0f](https://github.com/terraform-routeros/terraform-provider-routeros/commit/d98ce0f3aeb918ca02cbfc42eef6b9ba98ef7382)), closes [#474](https://github.com/terraform-routeros/terraform-provider-routeros/issues/474) ## [1.53.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.52.1...v1.53.0) (2024-05-29) ### Features * **logging-action:** Support for logging actions setup ([f8b9824](https://github.com/terraform-routeros/terraform-provider-routeros/commit/f8b9824a1f00ee456ab3a87793900922942daf42)), closes [#477](https://github.com/terraform-routeros/terraform-provider-routeros/issues/477) ### Bug Fixes * **certificate-scep-server:** Rename the resource from "routeros_certificate_scep_server" to "routeros_system_certificate_scep_server" ([a9a8138](https://github.com/terraform-routeros/terraform-provider-routeros/commit/a9a81385f2280fa1485d142b7c1e1d9dde541aff)), closes [#473](https://github.com/terraform-routeros/terraform-provider-routeros/issues/473) ## [1.52.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.52.0...v1.52.1) (2024-05-28) ### Bug Fixes * **firewall-raw:** Fix resource name ([e956f9f](https://github.com/terraform-routeros/terraform-provider-routeros/commit/e956f9ff5d72478f65e4db3e86808f689d0b3a40)) ## [1.52.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.51.0...v1.52.0) (2024-05-28) ### Features * **firewall/raw:** Add new resource ([90eb2fa](https://github.com/terraform-routeros/terraform-provider-routeros/commit/90eb2fa762a92e61bdb408095f7c2ef5a1c03e8e)), closes [#462](https://github.com/terraform-routeros/terraform-provider-routeros/issues/462) ### Bug Fixes * **dhcp-server:** Remove default values ([#470](https://github.com/terraform-routeros/terraform-provider-routeros/issues/470)) ([884e464](https://github.com/terraform-routeros/terraform-provider-routeros/commit/884e464d7f16f016b99c12371c2cbfca84a149fb)), closes [#466](https://github.com/terraform-routeros/terraform-provider-routeros/issues/466) ## [1.51.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.50.0...v1.51.0) (2024-05-21) ### Features * **x509:** Datasource for PEM data normalization and common_name extraction ([5f29176](https://github.com/terraform-routeros/terraform-provider-routeros/commit/5f29176d8109379bea87eeb65e8b49cbbc0ceffb)) * **x509:** Import certificates ([5a3bf8e](https://github.com/terraform-routeros/terraform-provider-routeros/commit/5a3bf8ed177e984a7b52322bd70a25431bfb42cd)), closes [#448](https://github.com/terraform-routeros/terraform-provider-routeros/issues/448) ## [1.50.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.49.0...v1.50.0) (2024-05-17) ### Features * **ovpn:** Add routeros_interface_ovpn_client ([85fd6be](https://github.com/terraform-routeros/terraform-provider-routeros/commit/85fd6be76bfb5a474f419ff226a969a40bc90c92)), closes [#452](https://github.com/terraform-routeros/terraform-provider-routeros/issues/452) ## [1.49.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.48.3...v1.49.0) (2024-05-17) ### Features * **clock:** Add routeros_system_clock ([e7b3131](https://github.com/terraform-routeros/terraform-provider-routeros/commit/e7b3131606bb1fb6e957c3e4ed68f352f674fa23)), closes [#453](https://github.com/terraform-routeros/terraform-provider-routeros/issues/453) ### Bug Fixes * **vrf:** Change import method ([915df28](https://github.com/terraform-routeros/terraform-provider-routeros/commit/915df28f7489e35b97c401f1ff4b8fbe1a223826)) * Warnings ([7fe815b](https://github.com/terraform-routeros/terraform-provider-routeros/commit/7fe815b91311a4b62ca0f42940b8cbdca938fbea)) ## [1.48.3](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.48.2...v1.48.3) (2024-05-14) ### Bug Fixes * Add comment to routeros_wifi resource ([#455](https://github.com/terraform-routeros/terraform-provider-routeros/issues/455)) ([5a2782c](https://github.com/terraform-routeros/terraform-provider-routeros/commit/5a2782c30e01ce2e7fa17944058a68eed03e9a2b)) ## [1.48.2](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.48.1...v1.48.2) (2024-05-13) ### Bug Fixes * **veth:** Remove deprecated options ([8565c5b](https://github.com/terraform-routeros/terraform-provider-routeros/commit/8565c5b5325c5b6fd61bcf8a2e76aa53e586f4fa)) ## [1.48.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.48.0...v1.48.1) (2024-05-09) ### Bug Fixes * **dns-record:** resource "routeros_ip_dns_record" keeps updating ([a755ab0](https://github.com/terraform-routeros/terraform-provider-routeros/commit/a755ab090746e1456b1bb1b4060f240d4420de5e)), closes [#445](https://github.com/terraform-routeros/terraform-provider-routeros/issues/445) * **TimeEquall:** Crash when using store_leases_disk in routeros_ip_dhcp_server_config ([ba8c5a9](https://github.com/terraform-routeros/terraform-provider-routeros/commit/ba8c5a92b31e25a134d369e400b2a883e774d692)), closes [#447](https://github.com/terraform-routeros/terraform-provider-routeros/issues/447) ## [1.48.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.47.0...v1.48.0) (2024-05-07) ### Features * **vrf:** Added routeros_ip_vrf resource ([#443](https://github.com/terraform-routeros/terraform-provider-routeros/issues/443)) ([a091b7d](https://github.com/terraform-routeros/terraform-provider-routeros/commit/a091b7da83aa9a70d04e9c50c8e81c2b6286e8d3)) ## [1.47.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.46.3...v1.47.0) (2024-05-04) ### Features * add ability to sign certificates with scep ([1dce5af](https://github.com/terraform-routeros/terraform-provider-routeros/commit/1dce5af9a010463974df011e8d530a792e29f8f2)) ### Bug Fixes * add challenge_password parameter for system_certificate resource ([d933589](https://github.com/terraform-routeros/terraform-provider-routeros/commit/d933589a186f86afad0ba609c05101ce26111149)) * fix changing sign data in routeros_system_certificate resource ([83848f9](https://github.com/terraform-routeros/terraform-provider-routeros/commit/83848f9dac2247c8b60ace492f62d448b85950cb)) ## [1.46.3](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.46.2...v1.46.3) (2024-04-27) ### Bug Fixes * properly unset firewall filter rule `protocol` field when removed ([#435](https://github.com/terraform-routeros/terraform-provider-routeros/issues/435)) ([03a017d](https://github.com/terraform-routeros/terraform-provider-routeros/commit/03a017d40e59f7ba94b6ad1e8b0f5f3236f28f9b)) ## [1.46.2](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.46.1...v1.46.2) (2024-04-26) ### Bug Fixes * **bridge_port:** [Backward compatibility] routeros_interface_bridge_port ([#436](https://github.com/terraform-routeros/terraform-provider-routeros/issues/436)) ([ad64040](https://github.com/terraform-routeros/terraform-provider-routeros/commit/ad64040358e5da70e326ab70d353008f263eb7fc)), closes [#419](https://github.com/terraform-routeros/terraform-provider-routeros/issues/419) [#419](https://github.com/terraform-routeros/terraform-provider-routeros/issues/419) ## [1.46.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.46.0...v1.46.1) (2024-04-21) ### Bug Fixes * **nat:** [Backward compatibility] resource "routeros_ip_firewall_nat" ([ee8f992](https://github.com/terraform-routeros/terraform-provider-routeros/commit/ee8f992098efb9bfdc636b81a4912b302046c839)), closes [#431](https://github.com/terraform-routeros/terraform-provider-routeros/issues/431) * **wg_peer:** Invalid syntax for 'client_keepalive' field ([df24de2](https://github.com/terraform-routeros/terraform-provider-routeros/commit/df24de28b406a1e431056266f6d5b429447d9f62)), closes [#432](https://github.com/terraform-routeros/terraform-provider-routeros/issues/432) ## [1.46.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.45.0...v1.46.0) (2024-04-16) ### Features * add routeros_certificate_scep_server resource ([#420](https://github.com/terraform-routeros/terraform-provider-routeros/issues/420)) ([b80b52d](https://github.com/terraform-routeros/terraform-provider-routeros/commit/b80b52d9ef5f8ce8d4b1396092ba1dc816765489)) ### Bug Fixes * **no-release:** [Backward compatibility] routeros_interface_bridge NOT WORKING as expected ([dd46e95](https://github.com/terraform-routeros/terraform-provider-routeros/commit/dd46e95ac82b18d3ff2f7ef5a2c1cba30dc6f2f1)), closes [#417](https://github.com/terraform-routeros/terraform-provider-routeros/issues/417) * **no-release:** [Backward compatibility] routeros_interface_bridge_port ([dca44bc](https://github.com/terraform-routeros/terraform-provider-routeros/commit/dca44bc1c23246fc63d2310f8f2a7d9f15ec5519)), closes [#419](https://github.com/terraform-routeros/terraform-provider-routeros/issues/419) * **no-release:** [Backward compatibility] routeros_ip_firewall_filter NOT WORKING AS EXPECTED ([6cc2072](https://github.com/terraform-routeros/terraform-provider-routeros/commit/6cc2072110ca5d67501a65ce5cd69cbb25e4fe12)), closes [#418](https://github.com/terraform-routeros/terraform-provider-routeros/issues/418) * **no-release:** Field 'tx_carrier_sense_error' not found in the schema ([c3d2eb2](https://github.com/terraform-routeros/terraform-provider-routeros/commit/c3d2eb2343e6593f673d57e2bbe80bd32fc863a9)), closes [#416](https://github.com/terraform-routeros/terraform-provider-routeros/issues/416) * **no-release:** Fix comparison of time & hex not provided by user ([4f85ccf](https://github.com/terraform-routeros/terraform-provider-routeros/commit/4f85ccf3303e6b91e6c0aee6dc0b4602d04604a3)) ## [1.45.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.44.3...v1.45.0) (2024-04-09) ### Features * **dhcp-relay:** Add DHCP Relay support ([6eb5901](https://github.com/terraform-routeros/terraform-provider-routeros/commit/6eb590183e2c67dc5f797bbfbe545d502f39a04c)), closes [#413](https://github.com/terraform-routeros/terraform-provider-routeros/issues/413) * **upnp:** Add UPNP & UPNP Interfaces ([8fd3da6](https://github.com/terraform-routeros/terraform-provider-routeros/commit/8fd3da66a671e1e3e6e62a38749fa0b58f3c1595)), closes [#412](https://github.com/terraform-routeros/terraform-provider-routeros/issues/412) ### Bug Fixes * Export/import certificates ([205a221](https://github.com/terraform-routeros/terraform-provider-routeros/commit/205a22110a712a123a6225ae0f71d9f763043c02)), closes [#404](https://github.com/terraform-routeros/terraform-provider-routeros/issues/404) * **upnp:** Fix "unknown parameter forced-external-ip" ([74333f2](https://github.com/terraform-routeros/terraform-provider-routeros/commit/74333f2ec7eeba08ff018084cf75003d102fac32)) ## [1.44.3](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.44.2...v1.44.3) (2024-04-09) ### Bug Fixes * **Importer:** Add extended resource import function ([713d9ee](https://github.com/terraform-routeros/terraform-provider-routeros/commit/713d9ee6318b0f71dea33207687b5062af605859)), closes [#403](https://github.com/terraform-routeros/terraform-provider-routeros/issues/403) ## [1.44.2](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.44.1...v1.44.2) (2024-04-04) ### Bug Fixes * Fix for release ([3c54e07](https://github.com/terraform-routeros/terraform-provider-routeros/commit/3c54e076d86ef551223c1d83b5fa72a0c5742db8)) ## [1.44.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.44.0...v1.44.1) (2024-04-04) ### Bug Fixes * routeros_ip_service not working ([c896837](https://github.com/terraform-routeros/terraform-provider-routeros/commit/c8968373e42e60bc1d0d53d23031ac8231086d7f)), closes [#407](https://github.com/terraform-routeros/terraform-provider-routeros/issues/407) ## [1.44.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.43.0...v1.44.0) (2024-04-04) ### Features * Add `routeros_capsman_interface` resource to manage CAPsMAN interfaces ([65b9717](https://github.com/terraform-routeros/terraform-provider-routeros/commit/65b9717b8afa7d218012f613af88c53fe0a21063)) ### Bug Fixes * Fix inline configuration properties not to update when untouched in `routeros_capsman_configuration` resources ([e2a5b55](https://github.com/terraform-routeros/terraform-provider-routeros/commit/e2a5b5527a77a99a84f35e8381a59add6ab63d11)) ## [1.43.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.42.0...v1.43.0) (2024-04-01) ### Features * Add NTP client resource ([0a678cf](https://github.com/terraform-routeros/terraform-provider-routeros/commit/0a678cf09c3ff350857c89c7d86cfc8a6de7cf00)), closes [#386](https://github.com/terraform-routeros/terraform-provider-routeros/issues/386) * Add IP neighbor discovery resource ([706bccb](https://github.com/terraform-routeros/terraform-provider-routeros/commit/706bccbec25c25f5be162f941664954769fe4cfd)), closes [#388](https://github.com/terraform-routeros/terraform-provider-routeros/issues/388) * Add SSH Server resource ([99883e4](https://github.com/terraform-routeros/terraform-provider-routeros/commit/99883e49dcd05ad9eca46c2042c2996c1225c938)), closes [#389](https://github.com/terraform-routeros/terraform-provider-routeros/issues/389) * Add tool/mac-server support ([13b565c](https://github.com/terraform-routeros/terraform-provider-routeros/commit/13b565c40f3178e57b475bdd85880d52c7f5eb3b)), closes [#387](https://github.com/terraform-routeros/terraform-provider-routeros/issues/387) ### Bug Fixes * Fix `isEmpty` function for Lists with ObjectType ([dbbb710](https://github.com/terraform-routeros/terraform-provider-routeros/commit/dbbb710f1ef97e3fed2ef7477e73a78e91104901)) * Set "AlwaysPresentNotUserProvided" for PropVrfRw ([fd58ea1](https://github.com/terraform-routeros/terraform-provider-routeros/commit/fd58ea19ae18b6fd2fbd1d1945b45c0936f1f0b9)) * **v7.12.2:** Change `routeros_interface_wireguard_peer` schema ([3f937c1](https://github.com/terraform-routeros/terraform-provider-routeros/commit/3f937c1b44d4661212b8aad93ebef326d52dbb55)) * **v7.12.2:** Change `routeros_ipv6_neighbor_discovery` schema ([598319a](https://github.com/terraform-routeros/terraform-provider-routeros/commit/598319adc29a02a34f6e4c52a87a979cc36a4a0b)) * **v7.12.2:** Change `routeros_routing_bgp_connection` schema ([205e43d](https://github.com/terraform-routeros/terraform-provider-routeros/commit/205e43dac4600fdf2e632380f9cd687c55cc06b4)) ## [1.42.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.41.0...v1.42.0) (2024-03-29) ### Features * Add `http` scheme support for the REST API ([c326052](https://github.com/terraform-routeros/terraform-provider-routeros/commit/c3260525afad9dad0e92b37ed6bb409a13e32d2e)) ## [1.41.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.40.0...v1.41.0) (2024-03-27) ### Features * Add function to create skip metadata based on slices ([ac54292](https://github.com/terraform-routeros/terraform-provider-routeros/commit/ac54292f53b6384efa5b7e4f2e1ce0761ce47b3e)) ### Bug Fixes * Fix fields in new commits ([205919f](https://github.com/terraform-routeros/terraform-provider-routeros/commit/205919f49fdb522357db562def3518194c3b81e4)) * Simplify the procedure for generating field conversion lists. ([a67b772](https://github.com/terraform-routeros/terraform-provider-routeros/commit/a67b772da55e9590390b92eb0e16b83041977058)) ## [1.40.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.39.0...v1.40.0) (2024-03-26) ### Features * Add `routeros_zerotier_controller` resource to manage ZeroTier controller ([68cb99a](https://github.com/terraform-routeros/terraform-provider-routeros/commit/68cb99a350261e5350f5bba987b5a42e8b6a637c)) * Add `routeros_zerotier_interface` resource to manage ZeroTier interface ([481709c](https://github.com/terraform-routeros/terraform-provider-routeros/commit/481709cee5b3a4fe3122173909e3ae19e43ccc18)) * Add `routeros_zerotier` resource to manage ZeroTier instances ([f182f0c](https://github.com/terraform-routeros/terraform-provider-routeros/commit/f182f0c93ab0ada397db2305eacd868e4a9b837a)) ## [1.39.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.38.0...v1.39.0) (2024-03-26) ### Features * Add `routeros_interface_wireless_cap` resource to manage CAPsMAN client ([b1558f9](https://github.com/terraform-routeros/terraform-provider-routeros/commit/b1558f92722ab69ccfe9cba86dc509f5ca591819)) * Add `routeros_ip_cloud_advanced` resource to manage advanced cloud settings ([405827d](https://github.com/terraform-routeros/terraform-provider-routeros/commit/405827d0d843431abe71fb303a65105ae29e86b0)) * Add `routeros_ppp_aaa` resource to manage authentication and accounting ([87f06c0](https://github.com/terraform-routeros/terraform-provider-routeros/commit/87f06c01c2a5b1b33bada1e4d6c69bcc559a38d3)) ## [1.38.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.37.2...v1.38.0) (2024-03-23) ### Features * Add support for interface macvlan ([24d940b](https://github.com/terraform-routeros/terraform-provider-routeros/commit/24d940b53e9b87487a19f7e7c44f7fe3a27e3c3f)) ### Bug Fixes * **no-release:** Resolve warnings due to some missing properties ([#381](https://github.com/terraform-routeros/terraform-provider-routeros/issues/381)) ([864ed27](https://github.com/terraform-routeros/terraform-provider-routeros/commit/864ed278e304e6789cb4401d4ef8ea33ca2e7bd2)) ## [1.37.2](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.37.1...v1.37.2) (2024-03-18) ### Bug Fixes * **dhcp-server:** Add a missing DHCP server option attribute ([c375754](https://github.com/terraform-routeros/terraform-provider-routeros/commit/c375754f0651196cbdb3ec986149d60f16fa6847)), closes [#376](https://github.com/terraform-routeros/terraform-provider-routeros/issues/376) ## [1.37.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.37.0...v1.37.1) (2024-03-18) ### Bug Fixes * Enable importing ethernet interfaces ([#379](https://github.com/terraform-routeros/terraform-provider-routeros/issues/379)) ([3676f3f](https://github.com/terraform-routeros/terraform-provider-routeros/commit/3676f3f210acaa51e6f0632369d8ba5f35f56b5e)) ## [1.37.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.36.1...v1.37.0) (2024-03-18) ### Features * Add `routeros_wifi` resource to manage WiFi interfaces ([5f234c4](https://github.com/terraform-routeros/terraform-provider-routeros/commit/5f234c456ca8c790e214d5b3e2dd8a8b1ab69b88)) ### Bug Fixes * Add reusable L2MTU property ([93a7495](https://github.com/terraform-routeros/terraform-provider-routeros/commit/93a749559aaf7df3235c8d9512cc7c5a1e417cea)) * Fix the `routeros_wifi_configuration` resource to suppress pristine inline parameters ([77d807c](https://github.com/terraform-routeros/terraform-provider-routeros/commit/77d807c2fd1e8bfe8d67de011c2b3b4851477d41)) * Refactor `AlwaysPresentNotUserProvided` helper to self-contain empty value check ([669fd68](https://github.com/terraform-routeros/terraform-provider-routeros/commit/669fd689008aba4673205fcfca1252c5b1a7f795)) * Refactor `AlwaysPresentNotUserProvided` helper to support map type ([038fe7c](https://github.com/terraform-routeros/terraform-provider-routeros/commit/038fe7c192ba87c46f1ed13334183f33d1cc6717)) ## [1.36.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.36.0...v1.36.1) (2024-03-18) ### Bug Fixes * stop using Int32MaxValue as upper bound for validation ([fcae1bd](https://github.com/terraform-routeros/terraform-provider-routeros/commit/fcae1bdfb8ef0b3622133eab4b04822b122ed405)) ## [1.36.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.35.1...v1.36.0) (2024-03-17) ### Features * Add routeros_system_script ([bf72b32](https://github.com/terraform-routeros/terraform-provider-routeros/commit/bf72b327d25605216ba8a5a44cb2aed43775dd6f)), closes [#373](https://github.com/terraform-routeros/terraform-provider-routeros/issues/373) ## [1.35.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.35.0...v1.35.1) (2024-02-28) ### Bug Fixes * **no-release:** Fix max value for MTU - Fix Main ([#365](https://github.com/terraform-routeros/terraform-provider-routeros/issues/365)) ([b2c57de](https://github.com/terraform-routeros/terraform-provider-routeros/commit/b2c57de6967d92dfc1dd445566f711544913483e)) * Undo commit "Change l2mtu property " ([#367](https://github.com/terraform-routeros/terraform-provider-routeros/issues/367)) ([8657054](https://github.com/terraform-routeros/terraform-provider-routeros/commit/86570540ecf5a3cdcd1a7391074e6626bbef8bc0)), closes [#326](https://github.com/terraform-routeros/terraform-provider-routeros/issues/326) ## [1.35.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.34.0...v1.35.0) (2024-02-22) ### Features * Add resource routeros_ipv6_neighbor_discovery ([#362](https://github.com/terraform-routeros/terraform-provider-routeros/issues/362)) ([13fb7b7](https://github.com/terraform-routeros/terraform-provider-routeros/commit/13fb7b75ba398b97551b58ff7cd8f99e26a15e12)) ### Bug Fixes * **no-release:** Add gateway6 field to /interface/veth ([#358](https://github.com/terraform-routeros/terraform-provider-routeros/issues/358)) ([b0385f6](https://github.com/terraform-routeros/terraform-provider-routeros/commit/b0385f66815ac1205606d64c3df80fcd9b315d08)) ## [1.34.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.33.1...v1.34.0) (2024-02-15) ### Features * **ipv6:** Add support for /ipv6/dhcp-client/option ([df43330](https://github.com/terraform-routeros/terraform-provider-routeros/commit/df43330ca7bed7e15d8a7f18d76adcacd3289fc0)) ## [1.33.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.33.0...v1.33.1) (2024-02-14) ### Features * **no-release:** Add support for /container ([#356](https://github.com/terraform-routeros/terraform-provider-routeros/issues/356)) ([afacc8f](https://github.com/terraform-routeros/terraform-provider-routeros/commit/afacc8fa8d382cf1db23336d448f141f8cabea56)) ### Bug Fixes * **ipv6:** Add fields for /ipv6/dhcp-client ([#357](https://github.com/terraform-routeros/terraform-provider-routeros/issues/357)) ([17a26d9](https://github.com/terraform-routeros/terraform-provider-routeros/commit/17a26d9a5c2f6a92415da688a64074f7bf97e9ce)) * **no-release:** Add missing ethernet interface fields to SkipFields ([#359](https://github.com/terraform-routeros/terraform-provider-routeros/issues/359)) ([55409c4](https://github.com/terraform-routeros/terraform-provider-routeros/commit/55409c4f762d2664da7b5cef7169e36013e2f203)) ## [1.33.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.32.6...v1.33.0) (2024-02-13) ### Features * **file:** Add support for /file ([#355](https://github.com/terraform-routeros/terraform-provider-routeros/issues/355)) ([2a1d9fd](https://github.com/terraform-routeros/terraform-provider-routeros/commit/2a1d9fd7f866b427fcbff4589b6b19d42c9267a1)) ### Bug Fixes * **no-release:** Change the Name property for the ipip resource ([4899cf1](https://github.com/terraform-routeros/terraform-provider-routeros/commit/4899cf18fae8dd85a3df64ac7b75d0d7752f4c52)) ## [1.32.6](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.32.5...v1.32.6) (2024-02-06) ### Features * **no-release:** add interface/ipip ([#346](https://github.com/terraform-routeros/terraform-provider-routeros/issues/346)) ([e7bd8dd](https://github.com/terraform-routeros/terraform-provider-routeros/commit/e7bd8ddaaf66ad101bd901d1c9045d52f69ab45b)) ### Bug Fixes * dhcp client add script string parameter ([#348](https://github.com/terraform-routeros/terraform-provider-routeros/issues/348)) ([3df2b69](https://github.com/terraform-routeros/terraform-provider-routeros/commit/3df2b69b155368383af121ac51a86f0d7b9a2896)) * **no-release:** Mandatory use of ID as ipip resource identifier ([55f8a62](https://github.com/terraform-routeros/terraform-provider-routeros/commit/55f8a629297512a8d6397de6786b9b6fb3320a6c)), closes [#346](https://github.com/terraform-routeros/terraform-provider-routeros/issues/346) ## [1.32.5](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.32.4...v1.32.5) (2024-01-25) ### Bug Fixes * Change l2mtu property ([9260354](https://github.com/terraform-routeros/terraform-provider-routeros/commit/92603541fd6b3f11dcbfd1ff2c1904f6687062a9)), closes [#326](https://github.com/terraform-routeros/terraform-provider-routeros/issues/326) * **cloud:** Add missing field ([235e3b0](https://github.com/terraform-routeros/terraform-provider-routeros/commit/235e3b07de33ca0b1edb82f728c0e3d241256a26)), closes [#331](https://github.com/terraform-routeros/terraform-provider-routeros/issues/331) * **no-release:** Fix Delete. Not an system object ([#343](https://github.com/terraform-routeros/terraform-provider-routeros/issues/343)) ([89ee0ea](https://github.com/terraform-routeros/terraform-provider-routeros/commit/89ee0ea2934f923dbf99744bd7d455bbf95910d2)) * **no-release:** Fix Delete. Not an Systemresource ([#342](https://github.com/terraform-routeros/terraform-provider-routeros/issues/342)) ([e560128](https://github.com/terraform-routeros/terraform-provider-routeros/commit/e560128554dd157b8b9a3399724baa2e5def521e)) * **wg peer:** Add missing fields ([48018c4](https://github.com/terraform-routeros/terraform-provider-routeros/commit/48018c4d525319b48e77923d57b4816ceb53cd30)), closes [#332](https://github.com/terraform-routeros/terraform-provider-routeros/issues/332) ## [1.32.4](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.32.3...v1.32.4) (2024-01-24) ### Bug Fixes * Removed all default values ([546764e](https://github.com/terraform-routeros/terraform-provider-routeros/commit/546764e1a14795936cd6890aad6530942b104c3e)), closes [#326](https://github.com/terraform-routeros/terraform-provider-routeros/issues/326) ## [1.32.3](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.32.2...v1.32.3) (2024-01-19) ### Bug Fixes * **switch:** Correct schema errors ([c4b3421](https://github.com/terraform-routeros/terraform-provider-routeros/commit/c4b3421fc39b2b1984aeab00b36c475844c4edf4)), closes [#325](https://github.com/terraform-routeros/terraform-provider-routeros/issues/325) ## [1.32.2](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.32.1...v1.32.2) (2024-01-16) ### Bug Fixes * **switch:** Incorrect procedure for deleting the resource ([1e2327c](https://github.com/terraform-routeros/terraform-provider-routeros/commit/1e2327c43d934da59fc2a6a36a485b4caf18db4d)), closes [#325](https://github.com/terraform-routeros/terraform-provider-routeros/issues/325) ## [1.32.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.32.0...v1.32.1) (2024-01-16) ### Bug Fixes * Add missing `default` property in `routeros_routing_bgp_template` ([0a2863f](https://github.com/terraform-routeros/terraform-provider-routeros/commit/0a2863ff01cfc26a904dab246716b4c9a7610f81)) * Add missing generated certificate properties in `routeros_capsman_manager` ([e4378af](https://github.com/terraform-routeros/terraform-provider-routeros/commit/e4378af30e5da4b1f4e6cb18cad5cb4b3ce61c2a)) * Add new `port_cost_mode` property to `routeros_interface_bridge` ([cab4fb6](https://github.com/terraform-routeros/terraform-provider-routeros/commit/cab4fb67e768b2e0b59c3ceac0edba07a1ba1138)) * Fix validator of the `address` property in `routeros_user_manager_router` ([1c7a46b](https://github.com/terraform-routeros/terraform-provider-routeros/commit/1c7a46b1bea703cafc5e1dd4782fce299d3383c6)) * Fix validator of the `advertise` property in `routeros_interface_ethernet` ([6c98bb7](https://github.com/terraform-routeros/terraform-provider-routeros/commit/6c98bb7360cbcaabbe3ef6d4688a0683577ef211)) ## [1.32.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.31.0...v1.32.0) (2024-01-15) ### Features * **switch:** Add support for /interface/ethernet/switch/host ([c267e5d](https://github.com/terraform-routeros/terraform-provider-routeros/commit/c267e5dbeab24578f3a7dbfcd9f5150b790391ba)), closes [#325](https://github.com/terraform-routeros/terraform-provider-routeros/issues/325) * **switch:** Add support for /interface/ethernet/switch/port ([a3e7921](https://github.com/terraform-routeros/terraform-provider-routeros/commit/a3e7921a1c59a6e4d79965ec3ec8bc1cf3f0ce09)), closes [#325](https://github.com/terraform-routeros/terraform-provider-routeros/issues/325) * **switch:** Add support for /interface/ethernet/switch/vlan ([2690373](https://github.com/terraform-routeros/terraform-provider-routeros/commit/2690373800ca29672b3c279fda4e99128e9f0860)), closes [#325](https://github.com/terraform-routeros/terraform-provider-routeros/issues/325) * **switch:** Add support for interface/ethernet/switch/rule ([163ebbe](https://github.com/terraform-routeros/terraform-provider-routeros/commit/163ebbe4d54e5a2d7444d015306ab0cdd4565055)), closes [#325](https://github.com/terraform-routeros/terraform-provider-routeros/issues/325) ## [1.31.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.30.0...v1.31.0) (2024-01-15) ### Features * Add WiFi access list resource ([e5a213a](https://github.com/terraform-routeros/terraform-provider-routeros/commit/e5a213abc3e10586471ddce5c0806a010997bcaa)) * Add Wifi accounting and authentication resource ([edd8299](https://github.com/terraform-routeros/terraform-provider-routeros/commit/edd8299edbad5b6c866d25659b262966d71fe09d)) * Add WiFi CAP resource ([d68fc6c](https://github.com/terraform-routeros/terraform-provider-routeros/commit/d68fc6c5c3d4207464446ee87451df00e4a885b1)) * Add WiFi CAPsMAN resource ([00197cf](https://github.com/terraform-routeros/terraform-provider-routeros/commit/00197cfc130d8316a43c351a66c1c757aaeb3c2b)) * Add WiFi channel resource ([3fd1f34](https://github.com/terraform-routeros/terraform-provider-routeros/commit/3fd1f34b040ee1e1eacce246a221a092315b8aea)) * Add WiFi configuration resource ([1505bff](https://github.com/terraform-routeros/terraform-provider-routeros/commit/1505bffb0d19c27553f7614b352dfc1a087c9806)) * Add WiFi datapath resource ([4193151](https://github.com/terraform-routeros/terraform-provider-routeros/commit/4193151e790ebe4b4b0074b3dd8e0cf5a4878ad8)) * Add WiFi interworking resource ([f748368](https://github.com/terraform-routeros/terraform-provider-routeros/commit/f748368c3ec2262eea531a51ec34706a30842f24)) * Add WiFi provisioning resource ([7a80781](https://github.com/terraform-routeros/terraform-provider-routeros/commit/7a80781f6bfe6bd1818c26a1f0f094317581fddc)) * Add WiFi security resource ([0c7e3af](https://github.com/terraform-routeros/terraform-provider-routeros/commit/0c7e3af28d23b27eff730331f0589484eef88b6d)) * Add WiFi steering resource ([3794b0f](https://github.com/terraform-routeros/terraform-provider-routeros/commit/3794b0fbf8bbd847deea81e5fc0554ffb010355f)) ## [1.30.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.29.1...v1.30.0) (2024-01-12) ### Features * **routing:** Manage Route Filters ([0c29e53](https://github.com/terraform-routeros/terraform-provider-routeros/commit/0c29e531be76e8c322062babd4c082d7e685ff7f)), closes [#330](https://github.com/terraform-routeros/terraform-provider-routeros/issues/330) ## [1.29.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.29.0...v1.29.1) (2024-01-11) ### Bug Fixes * **helpers:** Fix the style of writing filter parameters ([adf342f](https://github.com/terraform-routeros/terraform-provider-routeros/commit/adf342ff668cde440265fb34461d0d105602fc9f)) ## [1.29.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.28.1...v1.29.0) (2024-01-05) ### Features * Add system user authentication and accounting settings resource ([1899983](https://github.com/terraform-routeros/terraform-provider-routeros/commit/18999832de2ff864008b1f84cc0801fdfc9697c5)) * Add system user group resource ([c7cc658](https://github.com/terraform-routeros/terraform-provider-routeros/commit/c7cc658b69c5e75033c4eddfebc42f928bccd4e2)) * Add system user settings resource ([f889b7b](https://github.com/terraform-routeros/terraform-provider-routeros/commit/f889b7bd95bf4f559888edcfb32640b8d4fd731a)) ### Bug Fixes * Change the validator ([0b1881f](https://github.com/terraform-routeros/terraform-provider-routeros/commit/0b1881ff69cd3bc56a30d9fd2e239e08ee480b0d)) * Fix the growth of the 'valid' slice ([206cfc2](https://github.com/terraform-routeros/terraform-provider-routeros/commit/206cfc277a1ce5caaa9842d5b33503ee861745b1)) ## [1.28.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.28.0...v1.28.1) (2023-12-25) ### Bug Fixes * **firewall:** Fix the error of deleting field-lists ([3f2f7b1](https://github.com/terraform-routeros/terraform-provider-routeros/commit/3f2f7b1f6d335f99e3ff73e82f147a678325d4cf)) ## [1.28.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.27.2...v1.28.0) (2023-12-15) ### Features * Data source to list DHCP leases ([71f9571](https://github.com/terraform-routeros/terraform-provider-routeros/commit/71f95712255dcb1c1420c243c5a4426ba5328afd)), closes [#316](https://github.com/terraform-routeros/terraform-provider-routeros/issues/316) ### Bug Fixes * Comparison of MAC addresses in different character cases ([9acc3cc](https://github.com/terraform-routeros/terraform-provider-routeros/commit/9acc3cc705dd13a6e75d1487c1088eb7fea04609)) ## [1.27.2](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.27.1...v1.27.2) (2023-12-06) ### Bug Fixes * Firewall filter rules order ([#314](https://github.com/terraform-routeros/terraform-provider-routeros/issues/314)) ([3d32136](https://github.com/terraform-routeros/terraform-provider-routeros/commit/3d321362445fcfd401317f9829b1a206326dbae9)), closes [#293](https://github.com/terraform-routeros/terraform-provider-routeros/issues/293) ## [1.27.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.27.0...v1.27.1) (2023-12-06) ### Bug Fixes * Add 2.5Gbps to validation set ([#313](https://github.com/terraform-routeros/terraform-provider-routeros/issues/313)) ([47d6781](https://github.com/terraform-routeros/terraform-provider-routeros/commit/47d67811bc70776b9aea58f4f8ec0dc0166eed42)), closes [#311](https://github.com/terraform-routeros/terraform-provider-routeros/issues/311) ## [1.27.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.26.0...v1.27.0) (2023-12-02) ### Features * Add float type support ([4cc485b](https://github.com/terraform-routeros/terraform-provider-routeros/commit/4cc485b316747c59dee7e7efc3ddda32ee1c83b9)) * Add user manager advanced settings resource ([287db08](https://github.com/terraform-routeros/terraform-provider-routeros/commit/287db0886c31dfb1a91a7193ea944ffccc30747a)) * Add user manager attribute resource ([fa7dd30](https://github.com/terraform-routeros/terraform-provider-routeros/commit/fa7dd302356bafcb912d95eb41ec3ea339c4b78c)) * Add user manager database resource ([ca4e490](https://github.com/terraform-routeros/terraform-provider-routeros/commit/ca4e490fe75c85f41023d16f5316adef06c5cc37)) * Add user manager limitation resource ([a97bc45](https://github.com/terraform-routeros/terraform-provider-routeros/commit/a97bc45c75c99a525875046a01c2940069b624ba)) * Add user manager profile limitation resource ([733932c](https://github.com/terraform-routeros/terraform-provider-routeros/commit/733932c5bbca367fb5f461f3508b485ed428fb14)) * Add user manager profile resource ([d95bed1](https://github.com/terraform-routeros/terraform-provider-routeros/commit/d95bed153373122ef958d96a606b9199fa3f01f5)) * Add user manager router resource ([162e01e](https://github.com/terraform-routeros/terraform-provider-routeros/commit/162e01e556d9d92e0833ce470ff6486f645a0d34)) * Add user manager settings resource ([23761cf](https://github.com/terraform-routeros/terraform-provider-routeros/commit/23761cf5d2b13b28f93c6d2f2bc064f7156d58ca)) * Add user manager user group resource ([fff1568](https://github.com/terraform-routeros/terraform-provider-routeros/commit/fff156866c8f2a47edb52e407a4d129ad54a2185)) * Add user manager user profile resource ([9500635](https://github.com/terraform-routeros/terraform-provider-routeros/commit/95006359d071dc3c4ef2c3c19bf5847ab6f3dbbe)) * Add user manager user resource ([727cd9b](https://github.com/terraform-routeros/terraform-provider-routeros/commit/727cd9b196680d3649cccd506a7f88530aa735a4)) ### Bug Fixes * Fix the `AlwaysPresentNotUserProvided` helper to handle lists and sets correctly ([7c68003](https://github.com/terraform-routeros/terraform-provider-routeros/commit/7c68003df94e4fb3755101996e4418a41bb306b4)) ## [1.26.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.25.1...v1.26.0) (2023-11-29) ### Features * **/system/ntp/server:** Add NTP Server resource ([f7851ca](https://github.com/terraform-routeros/terraform-provider-routeros/commit/f7851ca3af4d26c3ebff5677153213b44b2d4657)), closes [#306](https://github.com/terraform-routeros/terraform-provider-routeros/issues/306) ## [1.25.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.25.0...v1.25.1) (2023-11-27) ### Bug Fixes * Add missing schema fields ([#303](https://github.com/terraform-routeros/terraform-provider-routeros/issues/303)) ([bd245b9](https://github.com/terraform-routeros/terraform-provider-routeros/commit/bd245b91820bc3920b38cdc984c7332c36ce03d0)) * **no-release:** Fix incorrect addition of skip fields ([9ee6d70](https://github.com/terraform-routeros/terraform-provider-routeros/commit/9ee6d70170d4c290609742ae27bac48228edaf7c)) ## [1.25.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.24.0...v1.25.0) (2023-11-15) ### Features * Add RADIUS incoming resource ([6fb0a23](https://github.com/terraform-routeros/terraform-provider-routeros/commit/6fb0a23a98f6089493a35031d9722b978110bb41)) * Add RADIUS resource ([eb0b5c3](https://github.com/terraform-routeros/terraform-provider-routeros/commit/eb0b5c38c0758efc866441fe630fc529f84af195)) ### Bug Fixes * Add compatibility layer for the VRF property in RADIUS incoming resource ([96a5354](https://github.com/terraform-routeros/terraform-provider-routeros/commit/96a5354b95f4f94d5198b55f0d017b8477c8f0b0)) * **no-release:** Skip computed stat fields ([#299](https://github.com/terraform-routeros/terraform-provider-routeros/issues/299)) ([579f0a0](https://github.com/terraform-routeros/terraform-provider-routeros/commit/579f0a0ea9cc73ab4c038b8bf9a2721ce6a9f99f)) ## [1.24.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.23.0...v1.24.0) (2023-11-13) ### Features * **ds:** Add /ip/arp datasource ([6ecd622](https://github.com/terraform-routeros/terraform-provider-routeros/commit/6ecd622973facf7296fd0009304c0249f1e3e369)) * **ds:** Add /system/resource datasource ([79a599e](https://github.com/terraform-routeros/terraform-provider-routeros/commit/79a599eb980bd73b677098d0cc54505b66b6069e)) ### Bug Fixes * **REST:** Return possible error on JSON parsing ([7134eac](https://github.com/terraform-routeros/terraform-provider-routeros/commit/7134eac723886d14214ef60de0faaa532387483c)) ## [1.23.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.22.1...v1.23.0) (2023-11-10) ### Features * Add 802.1X client resource ([db76369](https://github.com/terraform-routeros/terraform-provider-routeros/commit/db763696e4003399b76cb474ea32614a4e8028db)) * Add 802.1X server resource ([05894af](https://github.com/terraform-routeros/terraform-provider-routeros/commit/05894afc3145a8af232b3eb517c95ac224f2cfe4)) ### Bug Fixes * Bump Go version from 1.19 to 1.21 ([#297](https://github.com/terraform-routeros/terraform-provider-routeros/issues/297)) ([1a56503](https://github.com/terraform-routeros/terraform-provider-routeros/commit/1a565038228d408d55d405423288a54f006e967c)) ## [1.23.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.22.1...v1.23.0) (2023-11-10) ### Features * Add 802.1X client resource ([db76369](https://github.com/terraform-routeros/terraform-provider-routeros/commit/db763696e4003399b76cb474ea32614a4e8028db)) * Add 802.1X server resource ([05894af](https://github.com/terraform-routeros/terraform-provider-routeros/commit/05894afc3145a8af232b3eb517c95ac224f2cfe4)) ## [1.22.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.22.0...v1.22.1) (2023-11-09) ### Bug Fixes * Unexpected value: aes256 for RouterOS v7.7 ([#294](https://github.com/terraform-routeros/terraform-provider-routeros/issues/294)) ([6fc8149](https://github.com/terraform-routeros/terraform-provider-routeros/commit/6fc8149e77d45a53312e7d9c9b50b451d4791460)), closes [#291](https://github.com/terraform-routeros/terraform-provider-routeros/issues/291) ## [1.22.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.21.0...v1.22.0) (2023-11-07) ### Features * Add DHCP server config resource ([#288](https://github.com/terraform-routeros/terraform-provider-routeros/issues/288)) ([0e9fbbf](https://github.com/terraform-routeros/terraform-provider-routeros/commit/0e9fbbf52484962789bd28b2caaab9be238bff86)) ## [1.21.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.20.1...v1.21.0) (2023-11-06) ### Features * Add ethernet switch settings ([162c1da](https://github.com/terraform-routeros/terraform-provider-routeros/commit/162c1da0233e2e909d98dd02f011e89513233c9a)), closes [#285](https://github.com/terraform-routeros/terraform-provider-routeros/issues/285) [#282](https://github.com/terraform-routeros/terraform-provider-routeros/issues/282) * Add MLAG settings ([6b8cfd2](https://github.com/terraform-routeros/terraform-provider-routeros/commit/6b8cfd246d893abefc88ee3933e039f7cf1de508)), closes [#268](https://github.com/terraform-routeros/terraform-provider-routeros/issues/268) ### Bug Fixes * **bridge:** Add Name-Id migration ([84a7f3c](https://github.com/terraform-routeros/terraform-provider-routeros/commit/84a7f3c7cafeb700fe7fef8f367208b6d4ba2dc5)) * **CAPsMAN:** Add Name-Id migration ([5d0effa](https://github.com/terraform-routeros/terraform-provider-routeros/commit/5d0effa4d24365671dcbf268da66460b4483c25f)) * **dhcp_server:** Add Name-Id migration ([c8c9ff8](https://github.com/terraform-routeros/terraform-provider-routeros/commit/c8c9ff89d6b81253c7e8e8f020aba7cc47e03159)) * **eoip:** Add Name-Id migration ([fdbd68f](https://github.com/terraform-routeros/terraform-provider-routeros/commit/fdbd68f21f2ef5284b0e8529095a4cb5a3a76067)) * **eoip:** Fix the resource ID type ([7916c30](https://github.com/terraform-routeros/terraform-provider-routeros/commit/7916c30fcf36899631dfb25a362c378dc3ebbac4)) * **gre:** Add Name-Id migration ([6e811c3](https://github.com/terraform-routeros/terraform-provider-routeros/commit/6e811c3c3aa054f0c91d554cc9814ed3ff032b62)) * **interface_list:** Add Name-Id migration ([6b326c0](https://github.com/terraform-routeros/terraform-provider-routeros/commit/6b326c0dd62241e7b70307ac6400421f44ff94b1)) * **ip_pool:** Add Name-Id migration ([98c17c2](https://github.com/terraform-routeros/terraform-provider-routeros/commit/98c17c2130e798092a86e2e724b99c7a90980f15)) * **scheduler:** Add Name-Id migration ([c34b994](https://github.com/terraform-routeros/terraform-provider-routeros/commit/c34b99483f96eb9d1b49552326883fe9190c3c93)) * **vlan:** Add Name-Id migration ([7592f2f](https://github.com/terraform-routeros/terraform-provider-routeros/commit/7592f2f2fba7ea7d3a7875906b71a75126fd0f90)) * **vrrp:** Add Name-Id migration ([30c8f37](https://github.com/terraform-routeros/terraform-provider-routeros/commit/30c8f37a004c0b3b67dce85965535a0836b57af7)) * **wg:** Add Name-Id migration ([d676b8b](https://github.com/terraform-routeros/terraform-provider-routeros/commit/d676b8b26c64e114caeb108191ed911319c0f4ab)) ## [1.20.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.20.0...v1.20.1) (2023-11-02) ### Bug Fixes * Fix empty value check to handle default numeric values correctly ([#286](https://github.com/terraform-routeros/terraform-provider-routeros/issues/286)) ([661e49c](https://github.com/terraform-routeros/terraform-provider-routeros/commit/661e49ccbd0ca87eec024187e0e9ad6c2cb9890b)) * **no-release:** Some boolean params can't be reset and the provider does not understand the value ([#269](https://github.com/terraform-routeros/terraform-provider-routeros/issues/269)) ([678c9a4](https://github.com/terraform-routeros/terraform-provider-routeros/commit/678c9a4c67d2553e3bc72dc6a29d14d415520fa6)), closes [#253](https://github.com/terraform-routeros/terraform-provider-routeros/issues/253) ## [1.20.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.19.0...v1.20.0) (2023-10-31) ### Features * Add EoIP tunnel support ([#283](https://github.com/terraform-routeros/terraform-provider-routeros/issues/283)) ([bcab0fb](https://github.com/terraform-routeros/terraform-provider-routeros/commit/bcab0fb38634a992250ff92271543c5f0dd309cc)) ## [1.19.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.18.4...v1.19.0) (2023-10-29) ### Features * Add CAPsMAN access-list resource ([#281](https://github.com/terraform-routeros/terraform-provider-routeros/issues/281)) ([a0379c9](https://github.com/terraform-routeros/terraform-provider-routeros/commit/a0379c9b22edff87dc5dbedb1f74d8b30d010f09)) ## [1.18.4](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.18.3...v1.18.4) (2023-10-26) ### Bug Fixes * Fix enumerated values in CAPsMAN resources ([978576a](https://github.com/terraform-routeros/terraform-provider-routeros/commit/978576a59768e79369e1ba0fdff00044113260cd)) ## [1.18.3](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.18.2...v1.18.3) (2023-10-09) ### Bug Fixes * Fix double slash at the end of a hostname ([d33aa79](https://github.com/terraform-routeros/terraform-provider-routeros/commit/d33aa79a78d4932d2c8f89c854d0aa8940e6642c)), closes [#275](https://github.com/terraform-routeros/terraform-provider-routeros/issues/275) ## [1.18.2](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.18.1...v1.18.2) (2023-10-01) ### Bug Fixes * Improvements on the resource routeros_interface_ethernet ([#266](https://github.com/terraform-routeros/terraform-provider-routeros/issues/266)) ([099185b](https://github.com/terraform-routeros/terraform-provider-routeros/commit/099185beff487b514379a8472c0c208bdb6a6215)) ## [1.18.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.18.0...v1.18.1) (2023-09-27) ### Bug Fixes * Move WG keys from datasource to resource ([#265](https://github.com/terraform-routeros/terraform-provider-routeros/issues/265)) ([a4eaf8c](https://github.com/terraform-routeros/terraform-provider-routeros/commit/a4eaf8c5fd00e56f7d69ddac5bfa575e8487ff60)) ## [1.18.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.17.0...v1.18.0) (2023-09-24) ### Features * Creating key sets for WireGuard tunnels ([e2d28a3](https://github.com/terraform-routeros/terraform-provider-routeros/commit/e2d28a3d8d1184ab2fb4118cb7b44147cb8fbbc3)) ## [1.17.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.16.0...v1.17.0) (2023-09-22) ### Features * Ip firewall connection tracking ([#260](https://github.com/terraform-routeros/terraform-provider-routeros/issues/260)) ([9d39bf8](https://github.com/terraform-routeros/terraform-provider-routeros/commit/9d39bf82ebbff621888bb6535fe57148488f0215)) ## [1.16.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.15.0...v1.16.0) (2023-09-21) ### Features * Implement routeros_system_logging resource ([#261](https://github.com/terraform-routeros/terraform-provider-routeros/issues/261)) ([f8c89aa](https://github.com/terraform-routeros/terraform-provider-routeros/commit/f8c89aa7a197ae765fbbaf3138dda06b1a8787e4)) ## [1.15.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.14.0...v1.15.0) (2023-09-20) ### Features * Add routeros_ip_dhcp_server_option and routeros_ip_dhcp_server_option_set ([#259](https://github.com/terraform-routeros/terraform-provider-routeros/issues/259)) ([3722afb](https://github.com/terraform-routeros/terraform-provider-routeros/commit/3722afb8574250a7a7e3211f2e40f3b4acfdc56f)) ## [1.14.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.13.3...v1.14.0) (2023-09-19) ### Features * Implementation of routeos_interface_ethernet ([#256](https://github.com/terraform-routeros/terraform-provider-routeros/issues/256))([#255](https://github.com/terraform-routeros/terraform-provider-routeros/issues/255)) ([0d848bf](https://github.com/terraform-routeros/terraform-provider-routeros/commit/0d848bf4b3d12b438bc9cbb137c91dca616b9d6a)) ## [1.13.3](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.13.2...v1.13.3) (2023-09-18) ### Bug Fixes * ip_service drift + failure ([#257](https://github.com/terraform-routeros/terraform-provider-routeros/issues/257)) ([b53b31b](https://github.com/terraform-routeros/terraform-provider-routeros/commit/b53b31bc4ada26245c272a3e597c3ff4e4ed5d6c)), closes [#254](https://github.com/terraform-routeros/terraform-provider-routeros/issues/254) ## [1.13.2](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.13.1...v1.13.2) (2023-07-20) ### Bug Fixes * Add SNMP Settings ([#242](https://github.com/terraform-routeros/terraform-provider-routeros/issues/242)) ([e3a0d36](https://github.com/terraform-routeros/terraform-provider-routeros/commit/e3a0d36acb60c98088a298a21220746db0259dc4)), closes [#232](https://github.com/terraform-routeros/terraform-provider-routeros/issues/232) ## [1.13.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.13.0...v1.13.1) (2023-07-19) ### Bug Fixes * no updates when modifying the cod ([23d175b](https://github.com/terraform-routeros/terraform-provider-routeros/commit/23d175bf6ea2361330900628696f9c31641aebdb)), closes [#240](https://github.com/terraform-routeros/terraform-provider-routeros/issues/240) ## [1.13.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.12.1...v1.13.0) (2023-07-13) ### Features * Add an SNMP resource ([43c1ec9](https://github.com/terraform-routeros/terraform-provider-routeros/commit/43c1ec9f53a40b96f9bc47d7446e86c6c724f3e2)), closes [#232](https://github.com/terraform-routeros/terraform-provider-routeros/issues/232) * Add SNMP community resource ([eeea040](https://github.com/terraform-routeros/terraform-provider-routeros/commit/eeea04099b6431f508eb68cdd4d924653a7d17ff)) ## [1.12.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.12.0...v1.12.1) (2023-07-12) ### Bug Fixes * Fix the ParseDuration function ([1995d9e](https://github.com/terraform-routeros/terraform-provider-routeros/commit/1995d9ef8b8eac2808a34f3e433a24bd978e3bbc)) * Resource firewall filter ([fa04b82](https://github.com/terraform-routeros/terraform-provider-routeros/commit/fa04b820b7754d2e7cfdbab0d1064075415bf31d)), closes [#237](https://github.com/terraform-routeros/terraform-provider-routeros/issues/237) ## [1.12.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.11.0...v1.12.0) (2023-06-19) ### Features * Add IP Cloud ([#234](https://github.com/terraform-routeros/terraform-provider-routeros/issues/234)) ([675e9f3](https://github.com/terraform-routeros/terraform-provider-routeros/commit/675e9f3b57735ca035af34b43a0568fe2ee71c28)), closes [#231](https://github.com/terraform-routeros/terraform-provider-routeros/issues/231) ## [1.11.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.10.4...v1.11.0) (2023-06-19) ### Features * New OSPF resource ([4d473ea](https://github.com/terraform-routeros/terraform-provider-routeros/commit/4d473ea2e958a52f6544742cf47f0a1190e36508)) ### Bug Fixes * Add a helper for the attribute 'inactive' ([adca988](https://github.com/terraform-routeros/terraform-provider-routeros/commit/adca988ca08d36d6cab9a839fbad891827e72a81)) * Fix for error "no-summaries only valid for stubby areas" ([f222f71](https://github.com/terraform-routeros/terraform-provider-routeros/commit/f222f71691fd6e67b9967d66ef541e9d88376cea)) ## [1.10.4](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.10.3...v1.10.4) (2023-05-30) ### Bug Fixes * Patching firewall rules with place_before ([#224](https://github.com/terraform-routeros/terraform-provider-routeros/issues/224)) ([5ef738e](https://github.com/terraform-routeros/terraform-provider-routeros/commit/5ef738e1d530bb0c2aeb0abdeb8fd71f535150b7)), closes [#223](https://github.com/terraform-routeros/terraform-provider-routeros/issues/223) ## [1.10.3](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.10.2...v1.10.3) (2023-05-27) ### Bug Fixes * ci fix expired token ([#220](https://github.com/terraform-routeros/terraform-provider-routeros/issues/220)) ([e6a8585](https://github.com/terraform-routeros/terraform-provider-routeros/commit/e6a85853e60c4fafd49d59a7e702a9bae53f4678)) * **docs:** Update release.yml ([#221](https://github.com/terraform-routeros/terraform-provider-routeros/issues/221)) ([44ba77d](https://github.com/terraform-routeros/terraform-provider-routeros/commit/44ba77d1ffe8b9863a3e01a16379dea23406c8ee)), closes [#219](https://github.com/terraform-routeros/terraform-provider-routeros/issues/219) * Wrong field names in example files ([#219](https://github.com/terraform-routeros/terraform-provider-routeros/issues/219)) ([b0105ef](https://github.com/terraform-routeros/terraform-provider-routeros/commit/b0105ef1b295f7468debab14735e557e12eb01f3)), closes [#218](https://github.com/terraform-routeros/terraform-provider-routeros/issues/218) ## [1.10.2](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.10.1...v1.10.2) (2023-05-23) ### Bug Fixes * Remove extra space after passthrough in validation ([#217](https://github.com/terraform-routeros/terraform-provider-routeros/issues/217)) ([3061910](https://github.com/terraform-routeros/terraform-provider-routeros/commit/306191072d3ceb57acc4e0533ed878e1f6a18646)) ## [1.10.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.10.0...v1.10.1) (2023-05-19) ### Bug Fixes * ipv6 addr-list addr w/o netmask adds /128 netmask [#216](https://github.com/terraform-routeros/terraform-provider-routeros/issues/216) ([d6f7fad](https://github.com/terraform-routeros/terraform-provider-routeros/commit/d6f7fadfed9bac3e9bcc60640d935c08499053d2)) ## [1.10.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.9.1...v1.10.0) (2023-05-17) ### Features * Add support for /interface/pppoe-client ([#215](https://github.com/terraform-routeros/terraform-provider-routeros/issues/215)) ([a8cbe7d](https://github.com/terraform-routeros/terraform-provider-routeros/commit/a8cbe7d78ec868752f11184131c09b662e561a4c)), closes [#202](https://github.com/terraform-routeros/terraform-provider-routeros/issues/202) ## [1.9.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.9.0...v1.9.1) (2023-05-17) ### Bug Fixes * Field 'comment' not found in the schema ([#214](https://github.com/terraform-routeros/terraform-provider-routeros/issues/214)) ([01a7f10](https://github.com/terraform-routeros/terraform-provider-routeros/commit/01a7f101ade024981f8c59a56775aa1f4bdae442)), closes [#213](https://github.com/terraform-routeros/terraform-provider-routeros/issues/213) ## [1.9.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.8.0...v1.9.0) (2023-05-17) ### Features * Support ipv6 firewall address lists ([878fbf7](https://github.com/terraform-routeros/terraform-provider-routeros/commit/878fbf70d8e78c8993105da01f41a8ce8b9df4cb)), closes [#212](https://github.com/terraform-routeros/terraform-provider-routeros/issues/212) ## [1.8.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.7.2...v1.8.0) (2023-05-16) ### Features * Support bridge settings ([0bea447](https://github.com/terraform-routeros/terraform-provider-routeros/commit/0bea447ceed39579b565cd9041fdb98769e21f46)), closes [#209](https://github.com/terraform-routeros/terraform-provider-routeros/issues/209) ## [1.7.2](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.7.1...v1.7.2) (2023-05-15) ### Bug Fixes * nil pointer on bgp ([93cf45e](https://github.com/terraform-routeros/terraform-provider-routeros/commit/93cf45e45352b09cf24e82273d6905e10c0b1f13)), closes [#207](https://github.com/terraform-routeros/terraform-provider-routeros/issues/207) ## [1.7.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.7.0...v1.7.1) (2023-05-15) ### Bug Fixes * Fix resource names [#183](https://github.com/terraform-routeros/terraform-provider-routeros/issues/183) ([a4314d0](https://github.com/terraform-routeros/terraform-provider-routeros/commit/a4314d0c2765f7d6eea1cc672bf8c5dc633e9941)) * Fix the gateway field (veth) ([97b933b](https://github.com/terraform-routeros/terraform-provider-routeros/commit/97b933b2221005433cf249403086bce6d970c202)) ## [1.7.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.6.0...v1.7.0) (2023-05-14) ### Features * BGP connection ([3874d90](https://github.com/terraform-routeros/terraform-provider-routeros/commit/3874d909ff245e5493368a4e3d472e45cdcad65c)), closes [#183](https://github.com/terraform-routeros/terraform-provider-routeros/issues/183) * BGP templates ([7984574](https://github.com/terraform-routeros/terraform-provider-routeros/commit/7984574c9282019894e02b3f4b3fab04461c80a5)), closes [#183](https://github.com/terraform-routeros/terraform-provider-routeros/issues/183) * Processing nested fields in a list ([23928a0](https://github.com/terraform-routeros/terraform-provider-routeros/commit/23928a02c724d40b533ef16ec07deb2551497fb2)) * Support for /interface/bonding [#203](https://github.com/terraform-routeros/terraform-provider-routeros/issues/203) ([a7de21f](https://github.com/terraform-routeros/terraform-provider-routeros/commit/a7de21fd630450590a08be5b94449f82c33e6bbf)) * Support for veth interfaces [#206](https://github.com/terraform-routeros/terraform-provider-routeros/issues/206) ([a6fdcf8](https://github.com/terraform-routeros/terraform-provider-routeros/commit/a6fdcf80a2af0d2b14b66683fb578160e7555a99)) ### Bug Fixes * Changing the signature isEmpty + fixing the result for boolean values ([aedc90e](https://github.com/terraform-routeros/terraform-provider-routeros/commit/aedc90e5efbc6cc98adb558893cc17f727adeda9)) * Correct the logic of isEmpty ([18f4bf1](https://github.com/terraform-routeros/terraform-provider-routeros/commit/18f4bf19c58b4a5a8139e86946c254d7310ba013)) * Use helpers to process data for TypeMap ([280c994](https://github.com/terraform-routeros/terraform-provider-routeros/commit/280c994d060af676620cd592d26bcd988cc90405)) ## [1.6.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.5.0...v1.6.0) (2023-05-05) ### Features * Support creating users [#200](https://github.com/terraform-routeros/terraform-provider-routeros/issues/200) ([#201](https://github.com/terraform-routeros/terraform-provider-routeros/issues/201)) ([78191e2](https://github.com/terraform-routeros/terraform-provider-routeros/commit/78191e2038607af5081d06dfaabc208010f6d667)) ## [1.5.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.4.0...v1.5.0) (2023-05-04) ### Features * Add OpenVPN Server support ([6477fcd](https://github.com/terraform-routeros/terraform-provider-routeros/commit/6477fcdc61d5769a685fb32e551b41609f6f6aa6)) ### Bug Fixes * Rename the PropNameRw property and add a new one without forced re-creation ([a37f926](https://github.com/terraform-routeros/terraform-provider-routeros/commit/a37f926d2a1d8ffbddfbf5e75c5a28591f33e44c)) ## [1.4.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.3.9...v1.4.0) (2023-05-01) ### Features * Support for ip/services ([#195](https://github.com/terraform-routeros/terraform-provider-routeros/issues/195)) ([591096d](https://github.com/terraform-routeros/terraform-provider-routeros/commit/591096d4a249acb6f4484bc16a5aec691577453c)), closes [#182](https://github.com/terraform-routeros/terraform-provider-routeros/issues/182) ## [1.3.9](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.3.8...v1.3.9) (2023-05-01) ### Bug Fixes * Fix the creation of resources when renaming them ([c229d27](https://github.com/terraform-routeros/terraform-provider-routeros/commit/c229d27e27802507901381218585f27131db6c2a)), closes [#192](https://github.com/terraform-routeros/terraform-provider-routeros/issues/192) ## [1.3.8](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.3.7...v1.3.8) (2023-04-26) ### Bug Fixes * Warnings for primary_ntp and secondary_ntp when using routeros_ip_dhcp_client ([#190](https://github.com/terraform-routeros/terraform-provider-routeros/issues/190)) ([a7fc49f](https://github.com/terraform-routeros/terraform-provider-routeros/commit/a7fc49f1899f441fabbc63148a1b5c075cbaf27c)), closes [#189](https://github.com/terraform-routeros/terraform-provider-routeros/issues/189) ## [1.3.7](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.3.6...v1.3.7) (2023-04-23) ### Bug Fixes * Add `check_gateway` field to `routeros_ip_route` ([#187](https://github.com/terraform-routeros/terraform-provider-routeros/issues/187)) ([20b84ae](https://github.com/terraform-routeros/terraform-provider-routeros/commit/20b84aea4b4ce3af725ebd0f5165cca010f5692a)), closes [#186](https://github.com/terraform-routeros/terraform-provider-routeros/issues/186) ## [1.3.6](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.3.5...v1.3.6) (2023-04-22) ### Bug Fixes * ip/route dst_address should not be mandatory ([#185](https://github.com/terraform-routeros/terraform-provider-routeros/issues/185)) ([9cf42c7](https://github.com/terraform-routeros/terraform-provider-routeros/commit/9cf42c7ced6b813a9c0cf8465d1814ab1a5bce98)), closes [#184](https://github.com/terraform-routeros/terraform-provider-routeros/issues/184) ## [1.3.5](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.3.4...v1.3.5) (2023-04-20) ### Bug Fixes * Ability to set clamp-tcp-mss on mangle rule ([3226a91](https://github.com/terraform-routeros/terraform-provider-routeros/commit/3226a91963be950ba4111fd133b5e4f104a4fbbe)), closes [#178](https://github.com/terraform-routeros/terraform-provider-routeros/issues/178) * disabled mangle not seen ([ffb53d6](https://github.com/terraform-routeros/terraform-provider-routeros/commit/ffb53d66cb4edf8b7952f890c1d8e14f6f11b60b)), closes [#175](https://github.com/terraform-routeros/terraform-provider-routeros/issues/175) ## [1.3.4](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.3.3...v1.3.4) (2023-04-20) ### Bug Fixes * dns servers cannot be removed ([#179](https://github.com/terraform-routeros/terraform-provider-routeros/issues/179)) ([3db9080](https://github.com/terraform-routeros/terraform-provider-routeros/commit/3db9080137431e5031de9f27d53e8f022154f40a)), closes [#174](https://github.com/terraform-routeros/terraform-provider-routeros/issues/174) ## [1.3.3](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.3.2...v1.3.3) (2023-04-19) ### Bug Fixes * dns servers cannot be removed ([#177](https://github.com/terraform-routeros/terraform-provider-routeros/issues/177)) ([34a73be](https://github.com/terraform-routeros/terraform-provider-routeros/commit/34a73bed579e84568bfc24a3ead4bf8c1c62bbe9)), closes [#174](https://github.com/terraform-routeros/terraform-provider-routeros/issues/174) ## [1.3.2](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.3.1...v1.3.2) (2023-04-18) ### Bug Fixes * dns servers cannot be removed ([#176](https://github.com/terraform-routeros/terraform-provider-routeros/issues/176)) ([1ebc4d9](https://github.com/terraform-routeros/terraform-provider-routeros/commit/1ebc4d98072c86499bb972081ba1649e2af52ef0)), closes [#174](https://github.com/terraform-routeros/terraform-provider-routeros/issues/174) ## [1.3.1](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.3.0...v1.3.1) (2023-04-12) ### Bug Fixes * Remove default for VRRP interface group ([0cd9b5d](https://github.com/terraform-routeros/terraform-provider-routeros/commit/0cd9b5d41e10932ba631ae4d00b05c0ef948bbf0)) ## [1.3.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.2.0...v1.3.0) (2023-04-10) ### Features * Add support for a "certificate" resource ([898d2ad](https://github.com/terraform-routeros/terraform-provider-routeros/commit/898d2adf540ddcc04d4e535a36aee91fa3558fcd)) ## [1.2.0](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.1.11...v1.2.0) (2023-04-03) ### Features * Add support for CAPsMAN resources ([514b51f](https://github.com/terraform-routeros/terraform-provider-routeros/commit/514b51fd39250569e4c4112f7d349f98f885d743)) * Add support for composite types (TypeMap), TypeList.Int, TypeSet.Int, TypeSet.String ([8698a18](https://github.com/terraform-routeros/terraform-provider-routeros/commit/8698a18aa3179b281156c4fc311ba0ed5f5692a8)) * Add support for transforming the composite fields of Mikrotik. ([47d9ad3](https://github.com/terraform-routeros/terraform-provider-routeros/commit/47d9ad388753ff22eaee2bc83158920c37b60fd7)) * Add the default actions for system resources ([b3fb513](https://github.com/terraform-routeros/terraform-provider-routeros/commit/b3fb5138177b42e8dfb35a6d489f807fa8032be8)) ### Bug Fixes * Fix the import path ([a195c45](https://github.com/terraform-routeros/terraform-provider-routeros/commit/a195c45c1599b2e3f920c43cbc4e6dbabf8c895d)) * The 'disabled' property must be Computed (read-only) ([c4b85f6](https://github.com/terraform-routeros/terraform-provider-routeros/commit/c4b85f6ae3580f557acc4eaa14cacc371638ebda)) ## [1.1.11](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.1.10...v1.1.11) (2023-03-23) ### Bug Fixes * routeros_ip_dns - new fields in 7.8 ([#170](https://github.com/terraform-routeros/terraform-provider-routeros/issues/170)) ([c3d3eb3](https://github.com/terraform-routeros/terraform-provider-routeros/commit/c3d3eb3bdb9ac21bded109717bdba5075a1720ee)), closes [#169](https://github.com/terraform-routeros/terraform-provider-routeros/issues/169) ## [1.1.10](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.1.9...v1.1.10) (2023-03-21) ### Bug Fixes * Fix the order of document generation in CI ([3793cde](https://github.com/terraform-routeros/terraform-provider-routeros/commit/3793cde6785344aa9c5a092ed9142263a340949e)) ## [1.1.9](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.1.8...v1.1.9) (2023-03-21) ### Bug Fixes * Fix [#165](https://github.com/terraform-routeros/terraform-provider-routeros/issues/165) for REST responses containing escape sequences ([#167](https://github.com/terraform-routeros/terraform-provider-routeros/issues/167)) ([646ba4f](https://github.com/terraform-routeros/terraform-provider-routeros/commit/646ba4f6843904ec0122eb89285044104b051aa2)) ## [1.1.8](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.1.7...v1.1.8) (2023-03-12) ### Bug Fixes * routeros_dns_record does not change resource type and data correctly after [#158](https://github.com/terraform-routeros/terraform-provider-routeros/issues/158) ([4d95e80](https://github.com/terraform-routeros/terraform-provider-routeros/commit/4d95e80e73f8f494be7d3ea5fca382cc4e3f2fc5)), closes [#159](https://github.com/terraform-routeros/terraform-provider-routeros/issues/159) ## [1.1.7](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.1.6...v1.1.7) (2023-03-11) ### Bug Fixes * /ip/dns/static errors when trying to change the resource type ([0a935cd](https://github.com/terraform-routeros/terraform-provider-routeros/commit/0a935cd4affd9effdd8c0e8190415d055e4aafa9)), closes [#156](https://github.com/terraform-routeros/terraform-provider-routeros/issues/156) ## [1.1.6](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.1.5...v1.1.6) (2023-03-10) ### Bug Fixes * /ip/route - field disabled missing ([0baf464](https://github.com/terraform-routeros/terraform-provider-routeros/commit/0baf464ff8064fa38cf4458e4c56d3a0733f9865)), closes [#149](https://github.com/terraform-routeros/terraform-provider-routeros/issues/149) ## [1.1.5](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.1.4...v1.1.5) (2023-03-10) ### Bug Fixes * /ip/dns/record - field type is missing ([2072d14](https://github.com/terraform-routeros/terraform-provider-routeros/commit/2072d1486c2c1ded8259a628dc2e447a519a2a92)), closes [#150](https://github.com/terraform-routeros/terraform-provider-routeros/issues/150) ## [1.1.4](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.1.3...v1.1.4) (2023-03-04) ### Bug Fixes * Fix "post-test destroy" error ([38e79a4](https://github.com/terraform-routeros/terraform-provider-routeros/commit/38e79a497b1ff364ffc5c3b3a6e0d11c958d3616)) * Fix /ip/dhcp-server/network required field ([e9c69be](https://github.com/terraform-routeros/terraform-provider-routeros/commit/e9c69be9eca4b6791157c2a00bae0fdc436fec74)) ## [1.1.3](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.1.2...v1.1.3) (2023-02-24) ## [1.1.2](https://github.com/terraform-routeros/terraform-provider-routeros/compare/v1.1.1...v1.1.2) (2023-02-24) ## [1.1.1](https://github.com/GNewbury1/terraform-provider-routeros/compare/v1.1.0...v1.1.1) (2023-02-23) ### Bug Fixes * Set key correctly ([a299647](https://github.com/GNewbury1/terraform-provider-routeros/commit/a299647b7a23891bf6574bfe503bfa3b6d397cbd)) # [1.1.0](https://github.com/GNewbury1/terraform-provider-routeros/compare/v1.0.13...v1.1.0) (2023-02-23) ### Features * Add new signing key for new org ([7c0364a](https://github.com/GNewbury1/terraform-provider-routeros/commit/7c0364aa3bdfe3905cc7f588f9a114e98cbc76c8)) ## [1.0.13](https://github.com/GNewbury1/terraform-provider-routeros/compare/v1.0.12...v1.0.13) (2023-02-22) ### Bug Fixes * **#122:** Add missing fields to interface list member ([3debe19](https://github.com/GNewbury1/terraform-provider-routeros/commit/3debe192f123463e85a00f0318f92f7996d06906)), closes [#122](https://github.com/GNewbury1/terraform-provider-routeros/issues/122) ## [1.0.12](https://github.com/GNewbury1/terraform-provider-routeros/compare/v1.0.11...v1.0.12) (2023-02-22) ### Bug Fixes * **#106:** "root_path_cost" not found. ([4d568f5](https://github.com/GNewbury1/terraform-provider-routeros/commit/4d568f54db78297f3b71f4b1403f65214585c4ac)), closes [#106](https://github.com/GNewbury1/terraform-provider-routeros/issues/106) ## [1.0.11](https://github.com/GNewbury1/terraform-provider-routeros/compare/v1.0.10...v1.0.11) (2023-02-20) ### Bug Fixes * **#106:** Fix internal validation (for release). ([fa3bb93](https://github.com/GNewbury1/terraform-provider-routeros/commit/fa3bb93f22b22dbb6c50296cf6b12f030920f8d6)) ## [1.0.9](https://github.com/GNewbury1/terraform-provider-routeros/compare/v1.0.8...v1.0.9) (2023-02-20) ### Bug Fixes * **#106:** Added "multicast_router" field. ([128efe1](https://github.com/GNewbury1/terraform-provider-routeros/commit/128efe12b91fd2bf6a16536bd618047d4d6200b8)), closes [#106](https://github.com/GNewbury1/terraform-provider-routeros/issues/106) * **#110:** "host_name" set to Computed. ([d8c80dc](https://github.com/GNewbury1/terraform-provider-routeros/commit/d8c80dcc50dcee15f88aaa4a54574c4f5889a856)), closes [#110](https://github.com/GNewbury1/terraform-provider-routeros/issues/110) ## [1.0.8](https://github.com/GNewbury1/terraform-provider-routeros/compare/v1.0.7...v1.0.8) (2023-02-19) ### Bug Fixes * **#110:** Typo in hostname field for dhcp lease ([1113a36](https://github.com/GNewbury1/terraform-provider-routeros/commit/1113a3641e44fed551245415f387f1db34690d52)), closes [#110](https://github.com/GNewbury1/terraform-provider-routeros/issues/110) ## [1.0.7](https://github.com/GNewbury1/terraform-provider-routeros/compare/v1.0.6...v1.0.7) (2023-02-17) ### Bug Fixes * [#106](https://github.com/GNewbury1/terraform-provider-routeros/issues/106) /interface/bridge resource schema (IGMP snooping). ([dd9baaa](https://github.com/GNewbury1/terraform-provider-routeros/commit/dd9baaa9f81571c596c73f649919b6f475f6a327)) * [#109](https://github.com/GNewbury1/terraform-provider-routeros/issues/109) /interface/bridge/port resource schema (STP). ([182b067](https://github.com/GNewbury1/terraform-provider-routeros/commit/182b0679e0f9ec9996aff2cce170906c7cf5bf51)) * /ip/dhcp-server/lease resource schema. ([68a67d4](https://github.com/GNewbury1/terraform-provider-routeros/commit/68a67d48763e7bc8f7f5e9bd141044c5782637f4)) ## [1.0.7](https://github.com/GNewbury1/terraform-provider-routeros/compare/v1.0.6...v1.0.7) (2023-02-17) ### Bug Fixes * [#106](https://github.com/GNewbury1/terraform-provider-routeros/issues/106) /interface/bridge resource schema (IGMP snooping). ([dd9baaa](https://github.com/GNewbury1/terraform-provider-routeros/commit/dd9baaa9f81571c596c73f649919b6f475f6a327)) * [#109](https://github.com/GNewbury1/terraform-provider-routeros/issues/109) /interface/bridge/port resource schema (STP). ([182b067](https://github.com/GNewbury1/terraform-provider-routeros/commit/182b0679e0f9ec9996aff2cce170906c7cf5bf51)) * /ip/dhcp-server/lease resource schema. ([68a67d4](https://github.com/GNewbury1/terraform-provider-routeros/commit/68a67d48763e7bc8f7f5e9bd141044c5782637f4)) ## [1.0.6](https://github.com/GNewbury1/terraform-provider-routeros/compare/v1.0.5...v1.0.6) (2023-02-17) ### Bug Fixes * **#110:** Add missing fields to DhcpServerLease ([100af8f](https://github.com/GNewbury1/terraform-provider-routeros/commit/100af8f7da96ff38a879536f1894118fd9bc858d)), closes [#110](https://github.com/GNewbury1/terraform-provider-routeros/issues/110) ## [1.0.6](https://github.com/GNewbury1/terraform-provider-routeros/compare/v1.0.5...v1.0.6) (2023-02-17) ### Bug Fixes * **#110:** Add missing fields to DhcpServerLease ([100af8f](https://github.com/GNewbury1/terraform-provider-routeros/commit/100af8f7da96ff38a879536f1894118fd9bc858d)), closes [#110](https://github.com/GNewbury1/terraform-provider-routeros/issues/110) ## [1.0.5](https://github.com/GNewbury1/terraform-provider-routeros/compare/v1.0.4...v1.0.5) (2023-02-17) ### Bug Fixes * Spaces in resource names ([#102](https://github.com/GNewbury1/terraform-provider-routeros/issues/102) - [#104](https://github.com/GNewbury1/terraform-provider-routeros/issues/104)). ([6dafa4b](https://github.com/GNewbury1/terraform-provider-routeros/commit/6dafa4bd26ea406c5f4e481f201da1f16dd9b747)) ## [1.0.4](https://github.com/GNewbury1/terraform-provider-routeros/compare/v1.0.3...v1.0.4) (2023-02-16) ### Bug Fixes * Add gpg fingerprint to CI ([b315e13](https://github.com/GNewbury1/terraform-provider-routeros/commit/b315e130d338de82a1347c8f91cd4ba442d8d7c3)) ## [1.0.2](https://github.com/GNewbury1/terraform-provider-routeros/compare/v1.0.1...v1.0.2) (2023-02-15) ### Bug Fixes * Create multiple names for the same resource to aid compatibility ([5ed67a7](https://github.com/GNewbury1/terraform-provider-routeros/commit/5ed67a7e78cbf167320e2092c7c276e7410041bd)) * Interface child items had incorrect reference ([be14cb6](https://github.com/GNewbury1/terraform-provider-routeros/commit/be14cb6a2feec52cf5c34cc51924c1df09c90023)) ## [1.0.1](https://github.com/GNewbury1/terraform-provider-routeros/compare/v1.0.0...v1.0.1) (2023-02-14) ### Bug Fixes * IP validation fix ([12c1a23](https://github.com/GNewbury1/terraform-provider-routeros/commit/12c1a230aac5636b80636bd060c4024167d67f64)) ================================================ FILE: CODEOWNERS ================================================ * @terraform-routeros/provider-developers ================================================ FILE: LICENSE.md ================================================ Mozilla Public License Version 2.0 ================================== 1. Definitions -------------- 1.1. "Contributor" means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. 1.2. "Contributor Version" means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor's Contribution. 1.3. "Contribution" means Covered Software of a particular Contributor. 1.4. "Covered Software" means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. 1.5. "Incompatible With Secondary Licenses" means (a) that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or (b) that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License. 1.6. "Executable Form" means any form of the work other than Source Code Form. 1.7. "Larger Work" means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.8. "License" means this document. 1.9. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. 1.10. "Modifications" means any of the following: (a) any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or (b) any new file in Source Code Form that contains any Covered Software. 1.11. "Patent Claims" of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version. 1.12. "Secondary License" means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. 1.13. "Source Code Form" means the form of the work preferred for making modifications. 1.14. "You" (or "Your") means an individual or a legal entity exercising rights under this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. License Grants and Conditions -------------------------------- 2.1. Grants Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and (b) under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version. 2.2. Effective Date The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution. 2.3. Limitations on Grant Scope The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor: (a) for any code that a Contributor has removed from Covered Software; or (b) for infringements caused by: (i) Your and any other third party's modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or (c) under Patent Claims infringed by Covered Software in the absence of its Contributions. This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4). 2.4. Subsequent Licenses No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3). 2.5. Representation Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License. 2.6. Fair Use This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents. 2.7. Conditions Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1. 3. Responsibilities ------------------- 3.1. Distribution of Source Form All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients' rights in the Source Code Form. 3.2. Distribution of Executable Form If You distribute Covered Software in Executable Form then: (a) such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and (b) You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients' rights in the Source Code Form under this License. 3.3. Distribution of a Larger Work You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s). 3.4. Notices You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies. 3.5. Application of Additional Terms You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction. 4. Inability to Comply Due to Statute or Regulation --------------------------------------------------- If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 5. Termination -------------- 5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice. 5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination. ************************************************************************ * * * 6. Disclaimer of Warranty * * ------------------------- * * * * Covered Software is provided under this License on an "as is" * * basis, without warranty of any kind, either expressed, implied, or * * statutory, including, without limitation, warranties that the * * Covered Software is free of defects, merchantable, fit for a * * particular purpose or non-infringing. The entire risk as to the * * quality and performance of the Covered Software is with You. * * Should any Covered Software prove defective in any respect, You * * (not any Contributor) assume the cost of any necessary servicing, * * repair, or correction. This disclaimer of warranty constitutes an * * essential part of this License. No use of any Covered Software is * * authorized under this License except under this disclaimer. * * * ************************************************************************ ************************************************************************ * * * 7. Limitation of Liability * * -------------------------- * * * * Under no circumstances and under no legal theory, whether tort * * (including negligence), contract, or otherwise, shall any * * Contributor, or anyone who distributes Covered Software as * * permitted above, be liable to You for any direct, indirect, * * special, incidental, or consequential damages of any character * * including, without limitation, damages for lost profits, loss of * * goodwill, work stoppage, computer failure or malfunction, or any * * and all other commercial damages or losses, even if such party * * shall have been informed of the possibility of such damages. This * * limitation of liability shall not apply to liability for death or * * personal injury resulting from such party's negligence to the * * extent applicable law prohibits such limitation. Some * * jurisdictions do not allow the exclusion or limitation of * * incidental or consequential damages, so this exclusion and * * limitation may not apply to You. * * * ************************************************************************ 8. Litigation ------------- Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party's ability to bring cross-claims or counter-claims. 9. Miscellaneous ---------------- This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor. 10. Versions of the License --------------------------- 10.1. New Versions Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number. 10.2. Effect of New Versions You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward. 10.3. Modified Versions If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License). 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached. Exhibit A - Source Code Form License Notice ------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. Exhibit B - "Incompatible With Secondary Licenses" Notice --------------------------------------------------------- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. ================================================ FILE: Makefile ================================================ VERSION=$(shell git describe --tags --abbrev=0) EXT := ifeq ($(OS),Windows_NT) EXT := .exe endif .PHONY: docs debug all: docs tfformat compile checksum clean test: go test -timeout 30s github.com/terraform-routeros/terraform-provider-routeros docs: go generate ./... # !!! GNU Sed find docs -type f -exec sed -i -E '/^.*__[[:alpha:]_]+__/d' {} \; tfformat: terraform fmt -recursive examples/ debug: go generate routeros/provider.go go build -gcflags="all=-N -l" -o terraform-provider-routeros_${VERSION}$(EXT) main.go compile: mkdir -p pkg echo "Removing previously built packages" rm -rf pkg/* go generate routeros/provider.go echo "Compiling for every OS and Platform" GOOS=linux GOARCH=arm go build -o terraform-provider-routeros_${VERSION} main.go zip pkg/terraform-provider-routeros_${VERSION}_linux_arm.zip terraform-provider-routeros_${VERSION} GOOS=linux GOARCH=arm64 go build -o terraform-provider-routeros_${VERSION} main.go zip pkg/terraform-provider-routeros_${VERSION}_linux_arm64.zip terraform-provider-routeros_${VERSION} GOOS=linux GOARCH=386 go build -o terraform-provider-routeros_${VERSION} main.go zip pkg/terraform-provider-routeros_${VERSION}_linux_386.zip terraform-provider-routeros_${VERSION} GOOS=linux GOARCH=amd64 go build -o terraform-provider-routeros_${VERSION} main.go zip pkg/terraform-provider-routeros_${VERSION}_linux_amd64.zip terraform-provider-routeros_${VERSION} GOOS=windows GOARCH=amd64 go build -o terraform-provider-routeros_${VERSION}.exe main.go zip pkg/terraform-provider-routeros_${VERSION}_windows_amd64.zip terraform-provider-routeros_${VERSION}.exe GOOS=windows GOARCH=386 go build -o terraform-provider-routeros_${VERSION}.exe main.go zip pkg/terraform-provider-routeros_${VERSION}_windows_386.zip terraform-provider-routeros_${VERSION}.exe GOOS=darwin GOARCH=amd64 go build -o terraform-provider-routeros_${VERSION} main.go zip pkg/terraform-provider-routeros_${VERSION}_darwin_amd64.zip terraform-provider-routeros_${VERSION} GOOS=darwin GOARCH=arm64 go build -o terraform-provider-routeros_${VERSION} main.go zip pkg/terraform-provider-routeros_${VERSION}_darwin_arm64.zip terraform-provider-routeros_${VERSION} checksum: cd pkg && sha256sum *.zip > terraform-provider-routeros_${VERSION}_SHA256SUMS clean: rm terraform-provider-routeros_${VERSION} ================================================ FILE: README.md ================================================ # Terraform Provider RouterOS ![module testing workflow](https://github.com/GNewbury1/terraform-provider-routeros/actions/workflows/release.yml/badge.svg?branch=main) **Note**: In release 1.43, the resource schemas have been changed: * `routeros_routing_bgp_connection` * `routeros_ipv6_neighbor_discovery` * `routeros_interface_wireguard_peer` For the first two to work correctly, you must remove the resource state (`terraform state rm `) and import it again (`terraform import [options] `). ## Purpose This provider allows you to configure Mikrotik routers using [old API](https://help.mikrotik.com/docs/display/ROS/API) or [REST API](https://help.mikrotik.com/docs/display/ROS/REST+API), using or not using TLS. Compatibility testing is only performed within ROS version 7.x. From version 1.0.0, the provider has been rewritten by [vaerh](https://github.com/vaerh), and their [fork](https://github.com/vaerh/terraform-provider-routeros) has now been merged. This version drastically improves adding new endpoints to the provider, enabling significantly easier development. [vaerh](https://github.com/vaerh) has been added as a maintainer to this project. _We are not affiliated in any way with Mikrotik or the development of RouterOS_ ## Using the provider To get started with the provider, you first need to enable the REST API on your router. [You can follow the Mikrotik documentation on this](https://help.mikrotik.com/docs/display/ROS/REST+API), but the gist is to create an SSL cert (in `/system/certificates`) and enable the `web-ssl` service (in `/ip/services`) which uses that certificate. After that, include the following in your Terraform manifests: ```terraform terraform { required_providers { routeros = { source = "terraform-routeros/routeros" } } } provider "routeros" { hosturl = "(http|https|api|apis)://my.router.local[:port]" username = "my_username" password = "my_super_secret_password" } ``` For more in-depth documentation about each of the resources and datasources, please read the [documentation on Hashicorp's Provider registry](https://registry.terraform.io/providers/terraform-routeros/routeros/latest/docs) ### Versions tested - go 1.24.2 and ROS 7.12, 7.15, 7.16 (stable) ## Changelog For a detailed changelog, please see the [changelog.md](CHANGELOG.md). ## Contributing This version of the module greatly simplifies the process of adding new resources. You are welcome! ### Testing You can build the provider locally to test fixes by following these intructions: - Build and copy the provider where Terraform reads it ``` go build *.go && \ mkdir -p ~/.terraform.d/plugins/terraform.local/local/routeros/1.0.0/$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m) && \ mv main ~/.terraform.d/plugins/terraform.local/local/routeros/1.0.0/$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m)/terraform-provider-routeros_v1.0.0 ``` - Change provider from ```hcl required_providers { routeros = { source = "terraform-routeros/routeros" version = "1.85.1" } } ``` to ```hcl required_providers { routeros = { source = "terraform.local/local/routeros" version = "1.0.0" } } ``` - Clean your providers, init and apply - Alternatively, you can edit/create ~/.terraformrc add add a provider installation block like: ```hcl provider_installation { dev_overrides { "terraform-routeros/routeros" = "/path/to/your/git/clone" } direct { } } ``` and then build the provider using ``` go build -o terraform-provider-routeros *.go ``` in order for Terraform to find it. ### Fixing RouterOS property drift Sometimes RouterOS might introduce a breaking change on a property. You can easilfy contribute to the provider by following these intructions: - Edit `routeros/mikrotik_resource_drift.yaml`. Add the resource used as well as the old property name and the new one - Perform the generator. It should edit file `routeros/mikrotik_resource_drift.go`. ```bash cd routeros/ go run ../tools/drift/main.go ``` - Submit your changes! [Here](https://github.com/terraform-routeros/terraform-provider-routeros/pull/758/files) is a example of pull request. ================================================ FILE: docs/data-sources/files.md ================================================ # routeros_files (Data Source) ## Example Usage ```terraform data "routeros_files" "files" {} ``` ## Schema ### Optional - `filter` (Map of String) Additional request filtering options. ### Read-Only - `files` (List of Object) (see [below for nested schema](#nestedatt--files)) - `id` (String) The ID of this resource. ### Nested Schema for `files` Read-Only: - `contents` (String) - `creation_time` (String) - `id` (String) - `last_modified` (String) - `name` (String) - `package_architecture` (String) - `package_built_time` (String) - `package_name` (String) - `package_version` (String) - `size` (Number) - `type` (String) ================================================ FILE: docs/data-sources/firewall.md ================================================ # routeros_firewall (Data Source) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_ip_firewall](ip_firewall.md) ================================================ FILE: docs/data-sources/interface_bridge_filter.md ================================================ # routeros_interface_bridge_filter (Data Source) ## Example Usage ```terraform data "routeros_interface_bridge_filter" "rules" {} ``` ## Schema ### Optional - `filter` (Map of String) Additional request filtering options. ### Read-Only - `filters` (List of Object) (see [below for nested schema](#nestedatt--filters)) - `id` (String) The ID of this resource. ### Nested Schema for `filters` Read-Only: - `action` (String) - `bytes` (Number) - `chain` (String) - `comment` (String) - `dynamic` (Boolean) - `id` (String) - `in_interface` (String) - `invalid` (Boolean) - `mac_protocol` (String) - `packets` (Number) ================================================ FILE: docs/data-sources/interfaces.md ================================================ # routeros_interfaces (Data Source) ## Example Usage ```terraform data "routeros_interfaces" "interfaces" {} ``` ## Schema ### Optional - `filter` (Map of String) Additional request filtering options. ### Read-Only - `id` (String) The ID of this resource. - `interfaces` (List of Object) (see [below for nested schema](#nestedatt--interfaces)) ### Nested Schema for `interfaces` Read-Only: - `actual_mtu` (Number) - `comment` (String) - `default_name` (String) - `disabled` (Boolean) - `dynamic` (Boolean) - `id` (String) - `inactive` (Boolean) - `l2mtu` (Number) - `last_link_down_time` (String) - `last_link_up_time` (String) - `mac_address` (String) - `max_l2mtu` (Number) - `mtu` (String) - `name` (String) - `running` (Boolean) - `slave` (Boolean) - `type` (String) ================================================ FILE: docs/data-sources/ip_addresses.md ================================================ # routeros_ip_addresses (Data Source) ## Example Usage ```terraform data "routeros_ip_addresses" "ip_addresses" {} ``` ## Schema ### Optional - `filter` (Map of String) Additional request filtering options. ### Read-Only - `addresses` (List of Object) (see [below for nested schema](#nestedatt--addresses)) - `id` (String) The ID of this resource. ### Nested Schema for `addresses` Read-Only: - `actual_interface` (String) - `address` (String) - `comment` (String) - `disabled` (Boolean) - `dynamic` (Boolean) - `id` (String) - `interface` (String) - `invalid` (Boolean) - `network` (String) - `slave` (Boolean) ================================================ FILE: docs/data-sources/ip_arp.md ================================================ # routeros_ip_arp (Data Source) ## Example Usage ```terraform data "routeros_ip_arp" "data" {} ``` ## Schema ### Optional - `filter` (Map of String) Additional request filtering options. ### Read-Only - `data` (List of Object) (see [below for nested schema](#nestedatt--data)) - `id` (String) The ID of this resource. ### Nested Schema for `data` Read-Only: - `address` (String) - `complete` (Boolean) - `dhcp` (Boolean) - `disabled` (Boolean) - `dynamic` (Boolean) - `id` (String) - `interface` (String) - `invalid` (Boolean) - `mac_address` (String) - `published` (Boolean) ================================================ FILE: docs/data-sources/ip_dhcp_server_leases.md ================================================ # routeros_ip_dhcp_server_leases (Data Source) ## Example Usage ```terraform data "routeros_ip_dhcp_server_leases" "data" {} ``` ## Schema ### Optional - `filter` (Map of String) Additional request filtering options. ### Read-Only - `data` (List of Object) (see [below for nested schema](#nestedatt--data)) - `id` (String) The ID of this resource. ### Nested Schema for `data` Read-Only: - `active_address` (String) - `active_client_id` (String) - `active_mac_address` (String) - `active_server` (String) - `address` (String) - `address_lists` (String) - `age` (String) - `allow_dual_stack_queue` (Boolean) - `blocked` (Boolean) - `class_id` (String) - `client_id` (String) - `comment` (String) - `dhcp_option` (String) - `disabled` (Boolean) - `dynamic` (Boolean) - `expires_after` (String) - `host_name` (String) - `id` (String) - `last_seen` (String) - `mac_address` (String) - `radius` (Boolean) - `server` (String) - `status` (String) ================================================ FILE: docs/data-sources/ip_firewall.md ================================================ # routeros_ip_firewall (Data Source) This datasource contains all supported firewall resources: - address_list - nat - mangle - rules (aka filter) ## Example Usage ```terraform data "routeros_ip_firewall" "fw" { rules { filter = { chain = "input" comment = "rule_2" } } rules { filter = { chain = "forward" } } nat {} } output "rules" { value = [for value in data.routeros_ip_firewall.fw.rules : [value.id, value.src_address]] } output "nat" { value = [for value in data.routeros_ip_firewall.fw.nat : [value.id, value.comment]] } resource "routeros_ip_firewall" "rule_3" { action = "accept" chain = "input" comment = "rule_3" src_address = "192.168.0.5" place_before = data.routeros_ip_firewall_filter.fw.rules[0].id } ``` ## Schema ### Optional - `address_list` (Block List) (see [below for nested schema](#nestedblock--address_list)) - `mangle` (Block List) (see [below for nested schema](#nestedblock--mangle)) - `nat` (Block List) (see [below for nested schema](#nestedblock--nat)) - `rules` (Block List) (see [below for nested schema](#nestedblock--rules)) ### Read-Only - `id` (String) The ID of this resource. ### Nested Schema for `address_list` Optional: - `filter` (Map of String) Additional request filtering options. Read-Only: - `address` (String) - `comment` (String) - `creation_time` (String) - `disabled` (Boolean) - `dynamic` (Boolean) - `id` (String) - `list` (String) - `timeout` (String) ### Nested Schema for `mangle` Optional: - `filter` (Map of String) Additional request filtering options. Read-Only: - `action` (String) - `address_list` (String) - `address_list_timeout` (String) - `bytes` (Number) - `chain` (String) - `comment` (String) - `connection_bytes` (String) - `connection_limit` (String) - `connection_mark` (String) - `connection_nat_state` (String) - `connection_rate` (String) - `connection_state` (String) - `connection_type` (String) - `content` (String) - `disabled` (Boolean) - `dscp` (Number) - `dst_address` (String) - `dst_address_list` (String) - `dst_address_type` (String) - `dst_limit` (String) - `dst_port` (String) - `dynamic` (Boolean) - `fragment` (Boolean) - `hotspot` (String) - `icmp_options` (String) - `id` (String) - `in_bridge_port` (String) - `in_bridge_port_list` (String) - `in_interface` (String) - `in_interface_list` (String) - `ingress_priority` (Number) - `invalid` (Boolean) - `ipsec_policy` (String) - `ipv4_options` (String) - `jump_target` (String) - `layer7_protocol` (String) - `limit` (String) - `log` (Boolean) - `log_prefix` (String) - `new_connection_mark` (String) - `new_dscp` (Number) - `new_mss` (String) - `new_packet_mark` (String) - `new_priority` (String) - `new_routing_mark` (String) - `new_ttl` (String) - `nth` (String) - `out_bridge_port` (String) - `out_bridge_port_list` (String) - `out_interface` (String) - `out_interface_list` (String) - `packet_mark` (String) - `packet_size` (String) - `passthrough` (Boolean) - `per_connection_classifier` (String) - `port` (String) - `protocol` (String) - `psd` (String) - `random` (Number) - `route_dst` (String) - `routing_mark` (String) - `src_address` (String) - `src_address_list` (String) - `src_address_type` (String) - `src_mac_address` (String) - `src_port` (String) - `tcp_flags` (String) - `tcp_mss` (String) - `time` (String) - `tls_host` (String) - `ttl` (String) ### Nested Schema for `nat` Optional: - `filter` (Map of String) Additional request filtering options. Read-Only: - `action` (String) - `address_list` (String) - `address_list_timeout` (String) - `bytes` (Number) - `chain` (String) - `comment` (String) - `connection_bytes` (String) - `connection_limit` (String) - `connection_mark` (String) - `connection_rate` (String) - `connection_type` (String) - `content` (String) - `disabled` (Boolean) - `dscp` (Number) - `dst_address` (String) - `dst_address_list` (String) - `dst_address_type` (String) - `dst_limit` (String) - `dst_port` (String) - `dynamic` (Boolean) - `fragment` (Boolean) - `hotspot` (String) - `icmp_options` (String) - `id` (String) - `in_bridge_port` (String) - `in_bridge_port_list` (String) - `in_interface` (String) - `in_interface_list` (String) - `ingress_priority` (Number) - `invalid` (Boolean) - `ipsec_policy` (String) - `ipv4_options` (String) - `jump_target` (String) - `layer7_protocol` (String) - `limit` (String) - `log` (Boolean) - `log_prefix` (String) - `nth` (String) - `out_bridge_port` (String) - `out_bridge_port_list` (String) - `out_interface` (String) - `out_interface_list` (String) - `packet_mark` (String) - `packet_size` (String) - `per_connection_classifier` (String) - `port` (String) - `priority` (Number) - `protocol` (String) - `psd` (String) - `random` (Number) - `routing_mark` (String) - `same_not_by_dst` (Boolean) - `socks5_port` (String) - `socks5_server` (String) - `src_address` (String) - `src_address_list` (String) - `src_address_type` (String) - `src_mac_address` (String) - `src_port` (String) - `tcp_mss` (String) - `time` (String) - `to_addresses` (String) - `to_ports` (String) - `ttl` (String) ### Nested Schema for `rules` Optional: - `filter` (Map of String) Additional request filtering options. Read-Only: - `action` (String) - `address_list` (String) - `address_list_timeout` (String) - `bytes` (Number) - `chain` (String) - `comment` (String) - `connection_bytes` (String) - `connection_limit` (String) - `connection_mark` (String) - `connection_nat_state` (String) - `connection_rate` (String) - `connection_state` (String) - `connection_type` (String) - `content` (String) - `disabled` (Boolean) - `dscp` (Number) - `dst_address` (String) - `dst_address_list` (String) - `dst_address_type` (String) - `dst_limit` (String) - `dst_port` (String) - `dynamic` (Boolean) - `fragment` (Boolean) - `hotspot` (String) - `hw_offload` (Boolean) - `icmp_options` (String) - `id` (String) - `in_bridge_port` (String) - `in_bridge_port_list` (String) - `in_interface` (String) - `in_interface_list` (String) - `ingress_priority` (Number) - `invalid` (Boolean) - `ipsec_policy` (String) - `ipv4_options` (String) - `jump_target` (String) - `layer7_protocol` (String) - `limit` (String) - `log` (Boolean) - `log_prefix` (String) - `nth` (String) - `out_bridge_port` (String) - `out_bridge_port_list` (String) - `out_interface` (String) - `out_interface_list` (String) - `packet_mark` (String) - `packet_size` (String) - `per_connection_classifier` (String) - `port` (String) - `priority` (Number) - `protocol` (String) - `psd` (String) - `random` (Number) - `reject_with` (String) - `routing_mark` (String) - `routing_table` (String) - `src_address` (String) - `src_address_list` (String) - `src_address_type` (String) - `src_mac_address` (String) - `src_port` (String) - `tcp_flags` (String) - `tcp_mss` (String) - `time` (String) - `tls_host` (String) - `ttl` (String) ================================================ FILE: docs/data-sources/ip_routes.md ================================================ # routeros_ip_routes (Data Source) ## Example Usage ```terraform data "routeros_ip_routes" "ip_routes" {} ``` ## Schema ### Optional - `filter` (Map of String) Additional request filtering options. ### Read-Only - `id` (String) The ID of this resource. - `routes` (List of Object) (see [below for nested schema](#nestedatt--routes)) ### Nested Schema for `routes` Read-Only: - `active` (Boolean) - `blackhole` (Boolean) - `comment` (String) - `connect` (Boolean) - `dhcp` (Boolean) - `disabled` (Boolean) - `distance` (Number) - `dst_address` (String) - `dynamic` (Boolean) - `ecmp` (Boolean) - `gateway` (String) - `hw_offloaded` (Boolean) - `id` (String) - `immediate_gw` (String) - `inactive` (Boolean) - `local_address` (String) - `pref_src` (String) - `routing_table` (String) - `scope` (Number) - `static` (Boolean) - `suppress_hw_offload` (Boolean) - `target_scope` (Number) - `vrf_interface` (String) ================================================ FILE: docs/data-sources/ip_services.md ================================================ # routeros_ip_services (Data Source) ## Example Usage ```terraform data "routeros_ip_services" "router" { provider = routeros.router } resource "routeros_ip_service" "router-disabled" { provider = routeros.router for_each = { for s in data.routeros_ip_services.router.services : s.name => s if s.name != "www-ssl" } disabled = true numbers = each.value.name port = each.value.port } ``` ## Schema ### Optional - `filter` (Map of String) Additional request filtering options. ### Read-Only - `dynamic_services` (List of Object) (see [below for nested schema](#nestedatt--dynamic_services)) - `id` (String) The ID of this resource. - `services` (List of Object) (see [below for nested schema](#nestedatt--services)) ### Nested Schema for `dynamic_services` Read-Only: - `address` (String) - `certificate` (String) - `connection` (Boolean) - `disabled` (Boolean) - `dynamic` (Boolean) - `id` (String) - `invalid` (String) - `local` (String) - `max_sessions` (Number) - `name` (String) - `port` (Number) - `proto` (String) - `remote` (String) - `tls_version` (String) - `vrf` (String) ### Nested Schema for `services` Read-Only: - `address` (String) - `certificate` (String) - `connection` (Boolean) - `disabled` (Boolean) - `dynamic` (Boolean) - `id` (String) - `invalid` (String) - `max_sessions` (Number) - `name` (String) - `port` (Number) - `proto` (String) - `tls_version` (String) - `vrf` (String) ================================================ FILE: docs/data-sources/ipv6_addresses.md ================================================ # routeros_ipv6_addresses (Data Source) ## Example Usage ```terraform data "routeros_ipv6_addresses" "addresses" {} ``` ## Schema ### Optional - `filter` (Map of String) Additional request filtering options. ### Read-Only - `addresses` (List of Object) (see [below for nested schema](#nestedatt--addresses)) - `id` (String) The ID of this resource. ### Nested Schema for `addresses` Read-Only: - `actual_interface` (String) - `address` (String) - `advertise` (Boolean) - `comment` (String) - `deprecated` (Boolean) - `disabled` (Boolean) - `dynamic` (Boolean) - `eui_64` (Boolean) - `from_pool` (String) - `id` (String) - `interface` (String) - `invalid` (Boolean) - `link_local` (Boolean) - `no_dad` (Boolean) - `slave` (Boolean) ================================================ FILE: docs/data-sources/ipv6_firewall.md ================================================ # routeros_ipv6_firewall (Data Source) This datasource contains all supported firewall resources: - rules (aka filter) ## Schema ### Optional - `mangle` (Block List) (see [below for nested schema](#nestedblock--mangle)) - `nat` (Block List) (see [below for nested schema](#nestedblock--nat)) - `rules` (Block List) (see [below for nested schema](#nestedblock--rules)) ### Read-Only - `id` (String) The ID of this resource. ### Nested Schema for `mangle` Optional: - `filter` (Map of String) Additional request filtering options. Read-Only: - `action` (String) - `address_list` (String) - `address_list_timeout` (String) - `bytes` (Number) - `chain` (String) - `comment` (String) - `connection_bytes` (String) - `connection_limit` (String) - `connection_mark` (String) - `connection_nat_state` (String) - `connection_rate` (String) - `connection_state` (String) - `connection_type` (String) - `content` (String) - `disabled` (Boolean) - `dscp` (Number) - `dst_address` (String) - `dst_address_list` (String) - `dst_address_type` (String) - `dst_limit` (String) - `dst_port` (String) - `dynamic` (Boolean) - `icmp_options` (String) - `id` (String) - `in_bridge_port` (String) - `in_bridge_port_list` (String) - `in_interface` (String) - `in_interface_list` (String) - `ingress_priority` (Number) - `invalid` (Boolean) - `ipsec_policy` (String) - `jump_target` (String) - `limit` (String) - `log` (Boolean) - `log_prefix` (String) - `new_connection_mark` (String) - `new_dscp` (Number) - `new_mss` (String) - `new_packet_mark` (String) - `new_priority` (String) - `new_routing_mark` (String) - `new_ttl` (String) - `nth` (String) - `out_bridge_port` (String) - `out_bridge_port_list` (String) - `out_interface` (String) - `out_interface_list` (String) - `packet_mark` (String) - `packet_size` (String) - `passthrough` (Boolean) - `per_connection_classifier` (String) - `port` (String) - `priority` (Number) - `protocol` (String) - `random` (Number) - `routing_mark` (String) - `src_address` (String) - `src_address_list` (String) - `src_address_type` (String) - `src_mac_address` (String) - `src_port` (String) - `tcp_flags` (String) - `tcp_mss` (String) - `time` (String) - `tls_host` (String) - `ttl` (String) ### Nested Schema for `nat` Optional: - `filter` (Map of String) Additional request filtering options. Read-Only: - `action` (String) - `address_list` (String) - `address_list_timeout` (String) - `bytes` (Number) - `chain` (String) - `comment` (String) - `connection_bytes` (String) - `connection_limit` (String) - `connection_mark` (String) - `connection_rate` (String) - `connection_type` (String) - `content` (String) - `disabled` (Boolean) - `dscp` (Number) - `dst_address` (String) - `dst_address_list` (String) - `dst_address_type` (String) - `dst_limit` (String) - `dst_port` (String) - `dynamic` (Boolean) - `icmp_options` (String) - `id` (String) - `in_bridge_port` (String) - `in_bridge_port_list` (String) - `in_interface` (String) - `in_interface_list` (String) - `ingress_priority` (Number) - `invalid` (Boolean) - `ipsec_policy` (String) - `jump_target` (String) - `limit` (String) - `log` (Boolean) - `log_prefix` (String) - `nth` (String) - `out_bridge_port` (String) - `out_bridge_port_list` (String) - `out_interface` (String) - `out_interface_list` (String) - `packet_mark` (String) - `packet_size` (String) - `per_connection_classifier` (String) - `port` (String) - `priority` (Number) - `protocol` (String) - `random` (Number) - `routing_mark` (String) - `src_address` (String) - `src_address_list` (String) - `src_address_type` (String) - `src_mac_address` (String) - `src_port` (String) - `tcp_flags` (String) - `tcp_mss` (String) - `time` (String) - `tls_host` (String) - `to_address` (String) - `to_ports` (String) ### Nested Schema for `rules` Optional: - `filter` (Map of String) Additional request filtering options. Read-Only: - `action` (String) - `bytes` (Number) - `chain` (String) - `comment` (String) - `connection_bytes` (String) - `connection_limit` (String) - `connection_mark` (String) - `connection_nat_state` (String) - `connection_rate` (String) - `connection_state` (String) - `connection_type` (String) - `content` (String) - `disabled` (Boolean) - `dscp` (Number) - `dst_address` (String) - `dst_address_list` (String) - `dst_address_type` (String) - `dst_limit` (String) - `dst_port` (String) - `dynamic` (Boolean) - `icmp_options` (String) - `id` (String) - `in_bridge_port` (String) - `in_bridge_port_list` (String) - `in_interface` (String) - `in_interface_list` (String) - `ingress_priority` (Number) - `invalid` (Boolean) - `ipsec_policy` (String) - `limit` (String) - `log` (Boolean) - `log_prefix` (String) - `nth` (String) - `out_bridge_port` (String) - `out_bridge_port_list` (String) - `out_interface` (String) - `out_interface_list` (String) - `packet_mark` (String) - `packet_size` (String) - `per_connection_classifier` (String) - `port` (String) - `priority` (Number) - `protocol` (String) - `random` (Number) - `reject_with` (String) - `routing_mark` (String) - `routing_table` (String) - `src_address` (String) - `src_address_list` (String) - `src_address_type` (String) - `src_mac_address` (String) - `src_port` (String) - `tcp_flags` (String) - `tcp_mss` (String) - `time` (String) - `tls_host` (String) - `ttl` (String) ================================================ FILE: docs/data-sources/system_resource.md ================================================ # routeros_system_resource (Data Source) ## Example Usage ```terraform data "routeros_system_resource" "data" {} ``` ## Schema ### Optional ### Read-Only - `architecture_name` (String) - `board_name` (String) - `build_time` (String) - `cpu` (String) - `cpu_count` (Number) - `factory_software` (String) - `id` (String) The ID of this resource. - `platform` (String) - `total_hdd_space` (Number) - `total_memory` (Number) - `version` (String) ================================================ FILE: docs/data-sources/system_routerboard.md ================================================ # routeros_system_routerboard (Data Source) ## Example Usage ```terraform data "routeros_system_routerboard" "data" {} ``` ## Schema ### Optional ### Read-Only - `board_name` (String) - `current_firmware` (String) - `factory_firmware` (String) - `firmware_type` (String) - `id` (String) The ID of this resource. - `model` (String) - `revision` (String) - `routerboard` (Boolean) - `serial_number` (String) - `upgrade_firmware` (String) ================================================ FILE: docs/data-sources/wifi_easy_connect.md ================================================ # routeros_wifi_easy_connect (Data Source) ## Example Usage ```terraform data "routeros_wifi_easy_connect" "test" { type = "WPA2" ssid = "test" password = "password12345" } output "qrcode" { value = data.routeros_wifi_easy_connect.test.qr_code } # We can disable the QR code output and view it in the state file if needed. # terraform.exe state show data.routeros_wifi_easy_connect.test ``` ## Schema ### Required - `password` (String, Sensitive) Password, ignored if T is nopass (in which case it may be omitted). Enclose in double quotes if it is an ASCII name, but could be interpreted as hex (i.e. "ABCD"). - `ssid` (String) Network SSID. Required. Enclose in double quotes if it is an ASCII name, but could be interpreted as hex (i.e. "ABCD"). ### Optional - `eap_anonymous` (Boolean) (WPA2-EAP only) Anonymous identity - `eap_identity` (String) (WPA2-EAP only) Identity. - `eap_method` (String) (WPA2-EAP only) EAP method, like TTLS or PWD. - `eap_phase2` (String) (WPA2-EAP only) Phase 2 method, like `MSCHAPV2` - `hidden` (Boolean) True if the network SSID is hidden. - `type` (String) Authentication type; can be WEP or WPA or WPA2-EAP, or nopass for no password. Or, omit for no password. ### Read-Only - `id` (String) The ID of this resource. - `qr_code` (String) QR Code ================================================ FILE: docs/data-sources/x509.md ================================================ # routeros_x509 (Data Source) ## Example Usage ```terraform # You can keep indents in front of the content lines of the certificate. # The normalized certificate is available through the `pem` attribute data "routeros_x509" "cert" { data = < ## Schema ### Required - `data` (String) X509 certificate in PEM format. ### Read-Only - `akid` (String) - `authority` (Boolean) - `common_name` (String) - `digest_algorithm` (String) - `fingerprint` (String) - `id` (String) The ID of this resource. - `invalid_after` (String) - `invalid_before` (String) - `issuer` (String) - `key_type` (String) - `pem` (String) - `serial_number` (String) - `signature_algorithm` (String) - `skid` (String) - `subject` (String) - `subject_alt_name` (String) - `version` (Number) ================================================ FILE: docs/guides/certificate_rotation.md ================================================ # Certificate rotation Original [issue](https://github.com/terraform-routeros/terraform-provider-routeros/issues/584) ## Example ```terraform resource "tls_private_key" "ca_key" { algorithm = "RSA" } resource "tls_self_signed_cert" "ca_cert" { subject { common_name = "testCA" organization = "test" } private_key_pem = tls_private_key.ca_key.private_key_pem allowed_uses = ["digital_signature", "cert_signing", "crl_signing"] validity_period_hours = 24 * 365 * 5 is_ca_certificate = true } resource "tls_private_key" "server_key" { algorithm = "RSA" } resource "tls_cert_request" "server_csr" { private_key_pem = tls_private_key.server_key.private_key_pem subject { common_name = "mikrotik.example.com" organization = "test" } } resource "tls_locally_signed_cert" "server_cert" { cert_request_pem = tls_cert_request.server_csr.cert_request_pem ca_private_key_pem = tls_private_key.ca_key.private_key_pem ca_cert_pem = tls_self_signed_cert.ca_cert.cert_pem validity_period_hours = 12 allowed_uses = [ "key_encipherment", "digital_signature", "server_auth", ] } output "cert_serial_number_expected" { value = format("%x", tls_locally_signed_cert.server_cert.id) } resource "routeros_file" "server_key" { name = "server.key" contents = tls_private_key.server_key.private_key_pem } resource "routeros_file" "server_cert" { name = "server.crt" contents = tls_locally_signed_cert.server_cert.cert_pem } resource “routeros_system_certificate” “server_cert” { name = “server” common_name = tls_cert_request.server_csr.subject[0].common_name import { cert_file_name = routeros_file.server_cert.name key_file_name = routeros_file.server_key.name } depends_on = [routeros_file.server_cert, routeros_file.server_key] lifecycle { replace_triggered_by = [ tls_locally_signed_cert.server_cert.cert_pem ] } } output "cert_serial_nubmer_on_device" { value = routeros_system_certificate.server_cert.serial_number } ``` ================================================ FILE: docs/guides/easy_import.md ================================================ # Install package Original [issue](https://github.com/terraform-routeros/terraform-provider-routeros/issues/488) ## Example ```shell #!/bin/bash USER=admin PASS= HOST=http://router.local i=0 curl -s -u ${USER}:${PASS} ${HOST}/rest/ip/firewall/address-list | jq -c '.[] | select(.dynamic | ascii_downcase == "false") | {index: .".id", address: .address, comment: .comment, list: .list}' | while read rec; do index=$(echo $rec | jq .index) idx=$(printf "%00004d" $i) # echo $rec bash -cv "tofu state rm 'module.dev-gw0.routeros_ip_firewall_addr_list.address_list[\"$idx\"]'" bash -cv "tofu import 'module.dev-gw0.routeros_ip_firewall_addr_list.address_list[\"$idx\"]' $index" let i=${i}+1 done ``` ```terraform variable "address_list" { type = list(object({ address = string comment = optional(string) disabled = optional(bool, false) dynamic = optional(bool, false) list = string })) default = [ { address="192.168.88.11", comment="example 2", list="srv" }, { address="192.168.88.12", comment="example 2", list="srv" }, { address="192.168.88.1", comment="example", list="routeros" }, ] locals { # https://discuss.hashicorp.com/t/does-map-sort-keys/12056/2 # Map keys are always iterated in lexicographical order! address_list_map = { for idx, rule in var.address_list : format("%00004d", idx) => rule } } resource "routeros_ip_firewall_addr_list" "address_list" { for_each = local.address_list_map address = each.value.address comment = each.value.comment disabled = each.value.disabled list = each.value.list } ``` ================================================ FILE: docs/guides/install_package.md ================================================ # Install package The original example package installation is available in the [Schwitzd](https://github.com/Schwitzd/IaC-HomeRouter/blob/main/container_backend.tf) repository. ## Example ```terraform resource "null_resource" "download_container_npk" { provisioner "local-exec" { command = < /dev/null do echo "Waiting for router to reboot and become available..." sleep 10 done EOT } depends_on = [ null_resource.upload_container_npk ] } ``` ```shell #!/bin/bash # Input parameters ARCHITECTURE_NAME=$1 VERSION=$2 PACKAGE_NAME_PREFIX=$3 # Define the base URL and package format BASE_URL="https://download.mikrotik.com/routeros" PACKAGE_FORMAT="all_packages-${ARCHITECTURE_NAME}-${VERSION}.zip" # Construct the full URL FULL_URL="${BASE_URL}/${VERSION}/${PACKAGE_FORMAT}" # Define the download and extraction paths DOWNLOAD_PATH="/tmp/${PACKAGE_FORMAT}" EXTRACT_PATH="/tmp/routeros_packages" # Download the package echo "Downloading package from: ${FULL_URL}" curl -o "${DOWNLOAD_PATH}" "${FULL_URL}" # Verify download if [ $? -ne 0 ]; then echo "Failed to download the package." exit 1 fi # Create the extraction directory mkdir -p "${EXTRACT_PATH}" # List all files in the ZIP archive and filter by the PACKAGE_NAME_PREFIX echo "Finding package that starts with: ${PACKAGE_NAME_PREFIX}" MATCHED_FILES=$(unzip -l "${DOWNLOAD_PATH}" | awk '{print $4}' | grep "^${PACKAGE_NAME_PREFIX}") # Check if any files were matched if [ -z "$MATCHED_FILES" ]; then echo "No files found starting with '${PACKAGE_NAME_PREFIX}'." exit 1 fi # Extract matched files for FILE in $MATCHED_FILES; do echo "Extracting: ${FILE}" unzip -jo "${DOWNLOAD_PATH}" "${FILE}" -d "${EXTRACT_PATH}" if [ $? -ne 0 ]; then echo "Failed to extract: ${FILE}" exit 1 fi done echo "Extraction completed successfully in: ${EXTRACT_PATH}" ``` ================================================ FILE: docs/index.md ================================================ --- # generated by https://github.com/hashicorp/terraform-plugin-docs page_title: "RouterOS Provider" subcategory: "" description: |- A provider to integrate with the REST API introduced in RouterOS v7 --- # RouterOS Provider To get started with the provider, you first need to enable the REST API on your router. [You can follow the Mikrotik documentation on this](https://help.mikrotik.com/docs/display/ROS/REST+API), but the gist is to create an SSL cert (in `/system/certificates`) and enable the `web-ssl` service (in `/ip/services`) which uses that certificate. ## Example Usage ```terraform terraform { required_providers { routeros = { source = "terraform-routeros/routeros" } } } provider "routeros" { hosturl = "https://router.local" # env ROS_HOSTURL or MIKROTIK_HOST username = "admin" # env ROS_USERNAME or MIKROTIK_USER password = "" # env ROS_PASSWORD or MIKROTIK_PASSWORD ca_certificate = "/path/to/ca/certificate.pem" # env ROS_CA_CERTIFICATE or MIKROTIK_CA_CERTIFICATE insecure = true # env ROS_INSECURE or MIKROTIK_INSECURE } resource "routeros_interface_gre" "gre_hq" { name = "gre-hq-1" remote_address = "10.77.3.26" disabled = true } ``` ## Schema ### Required - `hosturl` (String) URL of the MikroTik router, default is TLS connection to REST. * API: api[s]://host[:port] * api://router.local * apis://router.local:8729 * REST: http[s]://host * http://router.local * https://router.local * router.local * 127.0.0.1 export ROS_HOSTURL=router.local or export MIKROTIK_HOST=router.local - `username` (String) Username for the MikroTik WEB/Winbox. export ROS_USERNAME=admin or export MIKROTIK_USER=admin ### Optional - `ca_certificate` (String) Path to MikroTik's certificate authority file (env: ROS_CA_CERTIFICATE | MIKROTIK_CA_CERTIFICATE). - `insecure` (Boolean) Whether to verify the SSL certificate or not (env: ROS_INSECURE | MIKROTIK_INSECURE). - `password` (String, Sensitive) Password for the MikroTik user (env: ROS_PASSWORD | MIKROTIK_PASSWORD). - `rest_timeout` (Number) HTTP Client Timeout - `routeros_version` (String) RouterOS version for which resource schemes will be adapted. The version obtained from MikroTik will be used if not specified (env: ROS_VERSION). - `suppress_syso_del_warn` (Boolean) Suppress the system object deletion warning (env: ROS_SUPPRESS_SYSO_DEL_WARN). ================================================ FILE: docs/resources/bridge.md ================================================ # routeros_bridge (Resource) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_interface_bridge](interface_bridge.md) ================================================ FILE: docs/resources/bridge_mlag.md ================================================ # routeros_bridge_mlag (Resource) ## Example Usage ```terraform resource "routeros_bridge_mlag" "mlag" { bridge = "bridge1" peer_port = "stack-link" } ``` ## Schema ### Required - `bridge` (String) The bridge interface where MLAG is being created. - `peer_port` (String) An interface that will be used as a peer port. Both peer devices are using inter-chassis communication over these peer ports to establish MLAG and update the host table. Peer port should be isolated on a different untagged VLAN using a pvid setting. Peer port can be configured as a bonding interface. ### Optional - `heartbeat` (String) This setting controls how often heartbeat messages are sent to check the connection between peers. If no heartbeat message is received for three intervals in a row, the peer logs a warning about potential communication problems. If set to none, heartbeat messages are not sent at all. - `priority` (Number) This setting changes the priority for selecting the primary MLAG node. A lower number means higher priority. If both MLAG nodes have the same priority, the one with the lowest bridge MAC address will become the primary device. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_bridge_mlag.mlag . ``` ================================================ FILE: docs/resources/bridge_port.md ================================================ # routeros_bridge_port (Resource) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_interface_bridge_port](interface_bridge_port.md) ================================================ FILE: docs/resources/bridge_vlan.md ================================================ # routeros_bridge_vlan (Resource) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_interface_bridge_vlan](interface_bridge_vlan.md) ================================================ FILE: docs/resources/capsman_aaa.md ================================================ # routeros_capsman_aaa (Resource) ## Example Usage ```terraform resource "routeros_capsman_aaa" "test_3a" { called_format = "ssid" mac_mode = "as-username-and-password" } ``` ## Schema ### Optional - `called_format` (String) Format of how the 'called-id' identifier will be passed to RADIUS. When configuring radius server clients, you can specify 'called-id' in order to separate multiple entires. - `interim_update` (String) When RADIUS accounting is used, Access Point periodically sends accounting information updates to the RADIUS server. This property specifies the default update interval that can be overridden by the RADIUS server using the Acct-Interim-Interval attribute. - `mac_caching` (String) If this value is set to a time interval, the Access Point will cache RADIUS MAC authentication responses for a specified time, and will not contact the RADIUS server if matching cache entry already exists. The value disabled will disable the cache, Access Point will always contact the RADIUS server. - `mac_format` (String) Controls how the MAC address of the client is encoded by Access Point in the User-Name attribute of the MAC authentication and MAC accounting RADIUS requests. - `mac_mode` (String) By default Access Point uses an empty password, when sending Access-Request during MAC authentication. When this property is set to as-username-and-password, Access Point will use the same value for the User-Password attribute as for the User-Name attribute. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_capsman_aaa.test_3a . ``` ================================================ FILE: docs/resources/capsman_access_list.md ================================================ # routeros_capsman_access_list (Resource) ## Example Usage ```terraform resource "routeros_capsman_datapath" "test_rule" { comment = "Catch-all" interface = "cap1" signal_range = "-120..-85" allow_signal_out_of_range = "20s" action = "reject" } ``` ## Schema ### Optional - `action` (String) An action to take when a client matches. - `allow_signal_out_of_range` (String) An option that permits the client's signal to be out of the range always or for some time interval. - `ap_tx_limit` (Number) Transmission speed limit in the direction of the client.. - `client_to_client_forwarding` (Boolean) An option that specifies whether to allow forwarding data between clients connected to the same interface. - `client_tx_limit` (Number) Transmission speed limit in the direction of the access point. - `comment` (String) - `disabled` (Boolean) - `interface` (String) Interface name to compare with an interface to which the client actually connects to. - `mac_address` (String) MAC address of the client. - `mac_mask` (String) MAC address mask to apply when comparing clients' addresses. - `place_before` (String) Before which position the rule will be inserted. > Please check the effect of this option, as it does not work as you think! > Best way to use in conjunction with a data source. See [example](../data-sources/ip_firewall.md#example-usage). - `private_passphrase` (String) PSK passphrase for the client if some PSK authentication algorithm is used. - `radius_accounting` (Boolean) An option that specifies if RADIUS traffic accounting should be used in case of RADIUS authentication of the client. - `signal_range` (String) The range in which the client signal must fall. - `ssid_regexp` (String) The regular expression to compare the actual SSID the client connects to. - `time` (String) Time of the day and days of the week when the rule is applicable. - `vlan_id` (Number) VLAN ID to use if vlan-mode enables use of VLAN tagging. - `vlan_mode` (String) VLAN tagging mode specifies if traffic coming from a client should get tagged and untagged when it goes back to the client. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/caps-man/access-list get [print show-ids]] terraform import routeros_capsman_access_list.test_rule "*1" #Or you can import a resource using one of its attributes terraform import routeros_capsman_access_list.test_rule "name=xxx" ``` ================================================ FILE: docs/resources/capsman_channel.md ================================================ # routeros_capsman_channel (Resource) ## Example Usage ```terraform resource "routeros_capsman_channel" "test_channel" { name = "test_channel" comment = "test_channel" band = "2ghz-b/g/n" control_channel_width = "10mhz" extension_channel = "eCee" frequency = [2412] reselect_interval = "1h" save_selected = true secondary_frequency = ["disabled"] skip_dfs_channels = true tx_power = 20 } ``` ## Schema ### Required - `name` (String) Changing the name of this resource will force it to be recreated. > The links of other configuration properties to this resource may be lost! > Changing the name of the resource outside of a Terraform will result in a loss of control integrity for that resource! ### Optional - `band` (String) Define operational radio frequency band and mode taken from hardware capability of wireless card. - `comment` (String) - `control_channel_width` (String) Control channel width. - `extension_channel` (String) Extension channel configuration. (E.g. Ce = extension channel is above Control channel, eC = extension channel is below Control channel) - `frequency` (List of Number) Channel frequency value in MHz on which AP will operate. If left blank, CAPsMAN will automatically determine the best frequency that is least occupied. - `reselect_interval` (String) The interval after which the least occupied frequency is chosen, can be defined as a random interval, ex. as '30m..60m'. Works only if channel.frequency is left blank. - `save_selected` (Boolean) If channel frequency is chosen automatically and channel.reselect-interval is used, then saves the last picked frequency. - `secondary_frequency` (List of String) Specifies the second frequency that will be used for 80+80MHz configuration. Set it to Disabled in order to disable 80+80MHz capability. - `skip_dfs_channels` (Boolean) If channel.frequency is left blank, the selection will skip DFS channels. - `tx_power` (Number) TX Power for CAP interface (for the whole interface not for individual chains) in dBm. It is not possible to set higher than allowed by country regulations or interface. By default max allowed by country or interface is used. - `width` (String) Channel Width in MHz. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/caps-man/channel get [print show-ids]] terraform import routeros_capsman_channel.test_channel "*1" #Or you can import a resource using one of its attributes terraform import routeros_capsman_channel.test_channel "name=xxx" ``` ================================================ FILE: docs/resources/capsman_configuration.md ================================================ # routeros_capsman_configuration (Resource) ## Example Usage ```terraform resource "routeros_capsman_configuration" "test_configuration" { comment = "Comment" country = "no_country_set" disconnect_timeout = "1s150ms" distance = "indoors" frame_lifetime = "0.12" // 120ms guard_interval = "long" hide_ssid = true hw_protection_mode = "rts-cts" hw_retries = 1 installation = "indoor" keepalive_frames = "enabled" load_balancing_group = "" max_sta_count = 1 mode = "ap" multicast_helper = "full" name = "test_configuration" rx_chains = [1, 3] ssid = "SSID" tx_chains = [0, 2] } resource "routeros_capsman_channel" "test_channel" { name = "test-channel-config" } resource "routeros_capsman_datapath" "test_datapath" { name = "test-datapath-config" } resource "routeros_capsman_rates" "test_rates" { name = "test-rates-config" } resource "routeros_capsman_security" "test_security" { name = "test-security-config" } resource "routeros_capsman_configuration" "test_configuration_2" { name = "test_configuration_name" channel = { config = "${routeros_capsman_channel.test_channel.name}" band = "2ghz-b/g/n" control_channel_width = "10mhz" extension_channel = "eCee" frequency = 2412 reselect_interval = "1h" save_selected = "true" secondary_frequency = "disabled" skip_dfs_channels = "true" tx_power = 20 } datapath = { config = "${routeros_capsman_datapath.test_datapath.name}" arp = "local-proxy-arp" bridge = "bridge" bridge_cost = "100" bridge_horizon = "200" client_to_client_forwarding = "true" interface_list = "static" l2mtu = "1450" local_forwarding = "true" mtu = "1500" vlan_id = "101" vlan_mode = "no-tag" // openflow_switch = "aaa" } rates = { config = "${routeros_capsman_rates.test_rates.name}" basic = "1Mbps,5.5Mbps,6Mbps,18Mbps,36Mbps,54Mbps" ht_basic_mcs = "mcs-0,mcs-7,mcs-11,mcs-14,mcs-16,mcs-21" ht_supported_mcs = "mcs-3,mcs-8,mcs-10,mcs-13,mcs-17,mcs-18" supported = "2Mbps,11Mbps,9Mbps,12Mbps,24Mbps,48Mbps" vht_basic_mcs = "none" vht_supported_mcs = "mcs0-9,mcs0-7" } security = { config = "${routeros_capsman_security.test_security.name}" authentication_types = "wpa-psk,wpa-eap" disable_pmkid = "true" eap_methods = "eap-tls,passthrough" eap_radius_accounting = "true" encryption = "aes-ccm,tkip" group_encryption = "aes-ccm" group_key_update = "1h" passphrase = "AAAAAAAAA" tls_certificate = "none" tls_mode = "verify-certificate" } depends_on = [ routeros_capsman_channel.test_channel, routeros_capsman_datapath.test_datapath, routeros_capsman_rates.test_rates, routeros_capsman_security.test_security ] } ``` ## Schema ### Required - `name` (String) Changing the name of this resource will force it to be recreated. > The links of other configuration properties to this resource may be lost! > Changing the name of the resource outside of a Terraform will result in a loss of control integrity for that resource! ### Optional - `channel` (Map of String) Channel inline settings. - `comment` (String) - `country` (String) Limits available bands, frequencies and maximum transmit power for each frequency. Also specifies default value of scan-list. Value no_country_set is an FCC compliant set of channels. - `datapath` (Map of String) Datapath inline settings. - `disconnect_timeout` (String) This interval is measured from third sending failure on the lowest data rate. At this point 3 * (hw-retries + 1) frame transmits on the lowest data rate had failed. During disconnect-timeout packet transmission will be retried with on-fail-retry-time interval. If no frame can be transmitted successfully during disconnect-timeout, the connection is closed, and this event is logged as "extensive data loss". Successful frame transmission resets this timer. - `distance` (String) How long to wait for confirmation of unicast frames (ACKs) before considering transmission unsuccessful, or in short ACK-Timeout. - `frame_lifetime` (String) Discard frames that have been queued for sending longer than frame-lifetime. By default, when value of this property is 0, frames are discarded only after connection is closed (format: 0.00 sec). - `guard_interval` (String) Whether to allow use of short guard interval (refer to 802.11n MCS specification to see how this may affect throughput). "any" will use either short or long, depending on data rate, "long" will use long. - `hide_ssid` (Boolean) This property has effect only in AP mode. Setting it to yes can remove this network from the list of wireless networks that are shown by some client software. Changing this setting does not improve the security of the wireless network, because SSID is included in other frames sent by the AP. - `hw_protection_mode` (String) Frame protection support property. [See docs](https://wiki.mikrotik.com/wiki/Manual:Interface/Wireless#Frame_protection_support_(RTS/CTS)). - `hw_retries` (Number) Number of times sending frame is retried without considering it a transmission failure. [See docs](https://wiki.mikrotik.com/wiki/Manual:Interface/Wireless) - `installation` (String) Adjusts scan-list to use indoor, outdoor or all frequencies for the country that is set. - `keepalive_frames` (String) If a client has not communicated for around 20 seconds, AP sends a "keepalive-frame". - `load_balancing_group` (String) Tags the interface to the load balancing group. For a client to connect to interface in this group, the interface should have the same number of already connected clients as all other interfaces in the group or smaller. Useful in setups where ranges of CAPs mostly overlap. - `max_sta_count` (Number) Maximum number of associated clients. - `mode` (String) Set operational mode. Only **ap** currently supported. - `multicast_helper` (String) When set to full multicast packets will be sent with unicast destination MAC address, resolving multicast problem on a wireless link. This option should be enabled only on the access point, clients should be configured in station-bridge mode. - `rates` (Map of String) Rates inline settings. - `rx_chains` (List of Number) Which antennas to use for receive. - `security` (Map of String) Security inline settings. - `ssid` (String) SSID (service set identifier) is a name broadcast in the beacons that identifies wireless network. - `tx_chains` (List of Number) Which antennas to use for transmit. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/caps-man/configuration get [print show-ids]] terraform import routeros_capsman_configuration.test_configuration_2 "*1" #Or you can import a resource using one of its attributes terraform import routeros_capsman_configuration.test_configuration_2 "name=xxx" ``` ================================================ FILE: docs/resources/capsman_datapath.md ================================================ # routeros_capsman_datapath (Resource) ## Example Usage ```terraform resource "routeros_capsman_datapath" "test_datapath" { name = "test_datapath" comment = "test_datapath" arp = "local-proxy-arp" bridge = "bridge" bridge_cost = 100 bridge_horizon = 200 client_to_client_forwarding = true interface_list = "static" l2mtu = 1450 local_forwarding = true mtu = 1500 vlan_id = 101 vlan_mode = "no-tag" // openflow_switch = "aaa" } ``` ## Schema ### Required - `name` (String) Changing the name of this resource will force it to be recreated. > The links of other configuration properties to this resource may be lost! > Changing the name of the resource outside of a Terraform will result in a loss of control integrity for that resource! ### Optional - `arp` (String) ARP mode. See [docs](https://wiki.mikrotik.com/wiki/Manual:IP/ARP#ARP_Modes) for info. - `bridge` (String) Bridge to which particular interface should be automatically added as port. Required only when local-forwarding is not used. - `bridge_cost` (Number) Bridge port cost to use when adding as bridge port. - `bridge_horizon` (Number) Bridge horizon to use when adding as bridge port. - `client_to_client_forwarding` (Boolean) Controls if client-to-client forwarding between wireless clients connected to interface should be allowed, in local forwarding mode this function is performed by CAP, otherwise it is performed by CAPsMAN. - `comment` (String) - `interface_list` (String) Interface list name. - `l2mtu` (Number) Layer2 MTU size. - `local_forwarding` (Boolean) Controls forwarding mode. If disabled, all L2 and L3 data will be forwarded to CAPsMAN, and further forwarding decisions will be made only then. See [docs](https://wiki.mikrotik.com/wiki/Manual:CAPsMAN#Local_Forwarding_Mode) for info. - `mtu` (Number) MTU size. - `openflow_switch` (String) OpenFlow switch to add interface to, as port when enabled. - `vlan_id` (Number) VLAN ID to assign to interface if vlan-mode enables use of VLAN tagging. - `vlan_mode` (String) VLAN tagging mode specifies if VLAN tag should be assigned to interface (causes all received data to get tagged with VLAN tag and allows interface to only send out data tagged with given tag) ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/caps-man/datapath get [print show-ids]] terraform import routeros_capsman_datapath.test_datapath "*1" #Or you can import a resource using one of its attributes terraform import routeros_capsman_datapath.test_datapath "name=xxx" ``` ================================================ FILE: docs/resources/capsman_interface.md ================================================ # routeros_capsman_interface (Resource) ## Example Usage ```terraform resource "routeros_capsman_channel" "channel1" { name = "1" band = "2ghz-g/n" frequency = [2412] } resource "routeros_capsman_interface" "cap1" { name = "cap1" channel = { config = routeros_capsman_channel.channel1.name } } ``` ## Schema ### Required - `name` (String) Name of the interface. ### Optional - `arp_timeout` (String) ARP timeout is time how long ARP record is kept in ARP table after no packets are received from IP. Value auto equals to the value of arp-timeout in IP/Settings, default is 30s. Can use postfix `ms`, `s`, `m`, `h`, `d` for milliseconds, seconds, minutes, hours or days. If no postfix is set then seconds (s) is used. - `channel` (Map of String) Channel inline settings. - `comment` (String) - `configuration` (Map of String) Configuration inline settings. - `datapath` (Map of String) Datapath inline settings. - `disabled` (Boolean) - `mac_address` (String) MAC address (BSSID) to use for the interface. - `master_interface` (String) The corresponding master interface of the virtual one. - `radio_mac` (String) The MAC address of the associated radio. - `radio_name` (String) Name of the associated radio. - `rates` (Map of String) Rates inline settings. - `security` (Map of String) Security inline settings. ### Read-Only - `bound` (Boolean) A flag whether the interface is currently available for the CAPsMAN. - `id` (String) The ID of this resource. - `inactive` (Boolean) A flag whether the interface is currently inactive. - `l2mtu` (Number) Layer2 Maximum transmission unit. [See](https://wiki.mikrotik.com/wiki/Maximum_Transmission_Unit_on_RouterBoards). - `master` (Boolean) A flag whether the interface is not a virtual one. - `running` (Boolean) A flag whether the interface has established a link to another device. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/caps-man/interface get [print show-ids]] terraform import routeros_capsman_interface.cap1 '*1' ``` ================================================ FILE: docs/resources/capsman_manager.md ================================================ # routeros_capsman_manager (Resource) ## Example Usage ```terraform resource "routeros_capsman_manager" "test_manager" { enabled = true upgrade_policy = "require-same-version" } ``` ## Schema ### Optional - `ca_certificate` (String) Device CA certificate. - `certificate` (String) Device certificate. - `enabled` (Boolean) Disable or enable CAPsMAN functionality. - `package_path` (String) Folder location for the RouterOS packages. For example, use '/upgrade' to specify the upgrade folder from the files section. If empty string is set, CAPsMAN can use built-in RouterOS packages, note that in this case only CAPs with the same architecture as CAPsMAN will be upgraded. - `require_peer_certificate` (Boolean) Require all connecting CAPs to have a valid certificate. - `upgrade_policy` (String) Upgrade policy options. ### Read-Only - `generated_ca_certificate` (String) Generated CA certificate. - `generated_certificate` (String) Generated CAPsMAN certificate. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_capsman_manager.test_manager . ``` ================================================ FILE: docs/resources/capsman_manager_interface.md ================================================ # routeros_capsman_manager_interface (Resource) ## Example Usage ```terraform resource "routeros_capsman_manager_interface" "test_manager_interface" { interface = "ether1" forbid = true } ``` ## Schema ### Required - `interface` (String) Name of the interface. ### Optional - `comment` (String) - `disabled` (Boolean) - `forbid` (Boolean) Disable interface listening. ### Read-Only - `default` (Boolean) It's the default item. - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/caps-man/manager/interface get [print show-ids]] terraform import routeros_capsman_manager_interface.test_manager_interface "*6" ``` ================================================ FILE: docs/resources/capsman_provisioning.md ================================================ # routeros_capsman_provisioning (Resource) ## Example Usage ```terraform resource "routeros_capsman_configuration" "test_configuration" { name = "cfg1" } resource "routeros_capsman_provisioning" "test_provisioning" { master_configuration = "cfg1" action = "create-disabled" name_prefix = "cap-" depends_on = [ routeros_capsman_configuration.test_configuration, ] } ``` ## Schema ### Required - `master_configuration` (String) If action specifies to create interfaces, then a new master interface with its configuration set to this configuration profile will be created ### Optional - `action` (String) Provisioning action. - `comment` (String) - `common_name_regexp` (String) Regular expression to match radios by common name. Each CAP's common name identifier can be found under "/caps-man radio" as value "REMOTE-CAP-NAME" - `disabled` (Boolean) - `hw_supported_modes` (Set of String) Match radios by supported wireless modes. - `identity_regexp` (String) Regular expression to match radios by router identity. - `ip_address_ranges` (Set of String) Match CAPs with IPs within configured address range. - `name_format` (String) Specify the syntax of the CAP interface name creation. - `name_prefix` (String) Name prefix which can be used in the name-format for creating the CAP interface names. - `radio_mac` (String) MAC address of radio to be matched, empty MAC (00:00:00:00:00:00) means match all MAC addresses. - `slave_configurations` (Set of String) If action specifies to create interfaces, then a new slave interface for each configuration profile in this list is created. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/caps-man/provisioning get [print show-ids]] terraform import routeros_capsman_provisioning.test_provisioning "*B" #Or you can import a resource using one of its attributes terraform import routeros_capsman_provisioning.test_provisioning "name=xxx" ``` ================================================ FILE: docs/resources/capsman_rates.md ================================================ # routeros_capsman_rates (Resource) ## Example Usage ```terraform resource "routeros_capsman_rates" "test_rates" { name = "test_rates" comment = "test_rates" basic = ["1Mbps", "5.5Mbps", "6Mbps", "18Mbps", "36Mbps", "54Mbps"] ht_basic_mcs = ["mcs-0", "mcs-7", "mcs-11", "mcs-14", "mcs-16", "mcs-21"] ht_supported_mcs = ["mcs-3", "mcs-8", "mcs-10", "mcs-13", "mcs-17", "mcs-18"] supported = ["2Mbps", "11Mbps", "9Mbps", "12Mbps", "24Mbps", "48Mbps"] vht_basic_mcs = "none" vht_supported_mcs = "mcs0-9,mcs0-7" } ``` ## Schema ### Required - `name` (String) Changing the name of this resource will force it to be recreated. > The links of other configuration properties to this resource may be lost! > Changing the name of the resource outside of a Terraform will result in a loss of control integrity for that resource! ### Optional - `basic` (Set of String) List of basic rates. Client will connect to AP only if it supports all basic rates announced by the AP. AP will establish WDS link only if it supports all basic rates of the other AP. - `comment` (String) - `ht_basic_mcs` (Set of String) Modulation and Coding Schemes that every connecting client must support. Refer to 802.11n for MCS specification. - `ht_supported_mcs` (Set of String) Modulation and Coding Schemes that this device advertises as supported. Refer to 802.11n for MCS specification. - `supported` (Set of String) List of supported rates. Two devices will communicate only using rates that are supported by both devices. - `vht_basic_mcs` (String) Modulation and Coding Schemes that every connecting client must support. Refer to 802.11ac for MCS specification. You can set MCS interval for each of Spatial Stream * none - will not use selected * MCS 0-7 - client must support MCS-0 to MCS-7 * MCS 0-8 - client must support MCS-0 to MCS-8 * MCS 0-9 - client must support MCS-0 to MCS-9 - `vht_supported_mcs` (String) Modulation and Coding Schemes that this device advertises as supported. Refer to 802.11ac for MCS specification. You can set MCS interval for each of Spatial Stream * none - will not use selected * MCS 0-7 - devices will advertise as supported MCS-0 to MCS-7 * MCS 0-8 - devices will advertise as supported MCS-0 to MCS-8 * MCS 0-9 - devices will advertise as supported MCS-0 to MCS-9 ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/caps-man/rates get [print show-ids]] terraform import routeros_capsman_rates.test_rates "*1" #Or you can import a resource using one of its attributes terraform import routeros_capsman_rates.test_rates "name=xxx" ``` ================================================ FILE: docs/resources/capsman_security.md ================================================ # routeros_capsman_security (Resource) ## Example Usage ```terraform resource "routeros_capsman_security" "test_security" { name = "test_security" comment = "test_security" authentication_types = ["wpa-psk", "wpa-eap", "wpa2-psk"] disable_pmkid = true eap_methods = "eap-tls,passthrough" eap_radius_accounting = true encryption = ["tkip", "aes-ccm"] group_encryption = "aes-ccm" group_key_update = "1h" passphrase = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE" tls_certificate = "none" tls_mode = "verify-certificate" } ``` ## Schema ### Required - `name` (String) Changing the name of this resource will force it to be recreated. > The links of other configuration properties to this resource may be lost! > Changing the name of the resource outside of a Terraform will result in a loss of control integrity for that resource! ### Optional - `authentication_types` (Set of String) Specify the type of Authentication from wpa-psk, wpa2-psk, wpa-eap or wpa2-eap. - `comment` (String) - `disable_pmkid` (Boolean) Whether to include PMKID into the EAPOL frame sent out by the Access Point. Disabling PMKID can cause compatibility issues with devices that use the PMKID to connect to an Access Point. - `eap_methods` (String) eap-tls - Use built-in EAP TLS authentication; passthrough - Access point will relay authentication process to the RADIUS server. - `eap_radius_accounting` (Boolean) Specifies if RADIUS traffic accounting should be used if RADIUS authentication gets done for this client - `encryption` (Set of String) Set type of unicast encryption algorithm used. - `group_encryption` (String) Access Point advertises one of these ciphers, multiple values can be selected. Access Point uses it to encrypt all broadcast and multicast frames. Client attempts connection only to Access Points that use one of the specified group ciphers. - `group_key_update` (String) Controls how often Access Point updates the group key. This key is used to encrypt all broadcast and multicast frames. property only has effect for Access Points. (30s..1h) - `passphrase` (String, Sensitive) WPA or WPA2 pre-shared key. - `tls_certificate` (String) Access Point always needs a certificate when security.tls-mode is set to value other than no-certificates. - `tls_mode` (String) This property has effect only when security.eap-methods contains eap-tls. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/caps-man/security get [print show-ids]] terraform import routeros_capsman_security.test_security "*1" #Or you can import a resource using one of its attributes terraform import routeros_capsman_security.test_security "name=xxx" ``` ================================================ FILE: docs/resources/certificate_scep_server.md ================================================ # routeros_certificate_scep_server (Resource) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_system_certificate_scep_server](system_certificate_scep_server.md) ================================================ FILE: docs/resources/container.md ================================================ # routeros_container (Resource) ## Example Usage ```terraform resource "routeros_container" "busybox" { remote_image = "library/busybox:1.35.0" cmd = "/bin/httpd -f -p 8080" interface = routeros_interface_veth.busybox.name logging = true root_dir = "/usb1-part1/containers/busybox/root" start_on_boot = true } ``` ## Schema ### Required - `interface` (String) veth interface to be used with the container ### Optional - `auto_restart_interval` (String) Specify an interval at which Container will be restarted on Container failure. - `check_certificate` (Boolean) Enables trust chain validation from local certificate store. - `cmd` (String) The main purpose of a CMD is to provide defaults for an executing container. These defaults can include an executable, or they can omit the executable, in which case you must specify an ENTRYPOINT instruction as well. - `comment` (String) - `devices` (Set of String) Passes through physical device to the container. - `dns` (String) Set custom DNS servers - `domain_name` (String) Container NIS domain name - `entrypoint` (String) An ENTRYPOINT allows to specify executable to run when starting container. Example: /bin/sh - `envlist` (String) list of environmental variables (configured under /container envs ) to be used with container - `file` (String) container *tar.gz tarball if the container is imported from a file - `hostname` (String) Container host name - `logging` (Boolean) if set to yes, all container-generated output will be shown in the RouterOS log - `memory_high` (String) RAM usage limit in bytes for a specific container (string value). - `mounts` (Set of String) Mounts from /container/mounts/ sub-menu to be used with this container - `remote_image` (String) The container image name to be installed if an external registry is used (configured under /container/config set registry-url=...) - `root_dir` (String) Used to save container store outside main memory - `running` (Boolean) Container state. - `start_on_boot` (Boolean) Start the container on boot - `stop_signal` (String) Signal to stop the container. - `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts)) - `user` (String) Sets the username used - `workdir` (String) The working directory for cmd entrypoint ### Read-Only - `arch` (String) The architecture of the container image - `id` (String) The ID of this resource. - `name` (String) Assign a name to the container - `os` (String) The OS of the container image - `status` (String) The status of the container - `tag` (String) The tag of the container image ### Nested Schema for `timeouts` Optional: - `create` (String) - `delete` (String) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/container get [print show-ids]] terraform import routeros_container.busybox "*1" ``` ================================================ FILE: docs/resources/container_config.md ================================================ # routeros_container_config (Resource) ## Example Usage ```terraform resource "routeros_container_config" "config" { registry_url = "https://registry-1.docker.io" ram_high = "0" tmpdir = "/usb1-part1/containers/tmp" layer_dir = "/usb1-part1/containers/layers" } ``` ## Schema ### Optional - `layer_dir` (String) Container layers directory. - `password` (String, Sensitive) Specifies the password for authentication (starting from ROS 7.8) - `ram_high` (String) RAM usage limit. - `registry_url` (String) External registry url from where the container will be downloaded. - `tmpdir` (String) Container extraction directory. - `username` (String) Specifies the username for authentication (starting from ROS 7.8) ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_container_config.config . ``` ================================================ FILE: docs/resources/container_envs.md ================================================ # routeros_container_envs (Resource) ## Example Usage ```terraform resource "routeros_container_envs" "test_envs" { name = "test_envs" key = "TZ" value = "UTC" } ``` ## Schema ### Required - `key` (String) Key of the environment variable. - `name` (String) Name of the environment variables list. - `value` (String) Value of the environment variable. ### Optional ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/container/envs get [print show-ids]] terraform import routeros_container_envs.test_envs "*1" #Or you can import a resource using one of its attributes terraform import routeros_container_envs.test_envs "name=xxx" ``` ================================================ FILE: docs/resources/container_mounts.md ================================================ # routeros_container_mounts (Resource) ## Example Usage ```terraform resource "routeros_container_mounts" "caddyfile" { name = "Caddyfile" src = "/usb1-part1/containers/caddy/Caddyfile" dst = "/etc/caddy/Caddyfile" } ``` ## Schema ### Required - `dst` (String) Specifies destination path of the mount, which points to defined location in container - `name` (String) Name of the mount. - `src` (String) Specifies source path of the mount, which points to a RouterOS location ### Optional ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell # Import with the name of the container mount in case of the example use Caddyfile terraform import routeros_container_mounts.caddyfile Caddyfile #Or you can import a resource using one of its attributes terraform import routeros_container_mounts.caddyfile "name=xxx" ``` ================================================ FILE: docs/resources/dhcp_client.md ================================================ # routeros_dhcp_client (Resource) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_ip_dhcp_client](ip_dhcp_client.md) ================================================ FILE: docs/resources/dhcp_client_option.md ================================================ # routeros_dhcp_client_option (Resource) ## Schema ### Required - `code` (Number) The dhcp-client option code. - `name` (String) The name that will be used in dhcp-client. ### Optional - `raw_value` (String) raw_value is computed from value. - `value` (String) The dhcp-client option ### Read-Only - `id` (String) The ID of this resource. ================================================ FILE: docs/resources/dhcp_server.md ================================================ # routeros_dhcp_server (Resource) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_ip_dhcp_server](ip_dhcp_server.md) ================================================ FILE: docs/resources/dhcp_server_lease.md ================================================ # routeros_dhcp_server_lease (Resource) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_ip_dhcp_server_lease](ip_dhcp_server_lease.md) ================================================ FILE: docs/resources/dhcp_server_network.md ================================================ # routeros_dhcp_server_network (Resource) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_ip_dhcp_server_network](ip_dhcp_server_network.md) ================================================ FILE: docs/resources/disk_settings.md ================================================ # routeros_disk_settings (Resource) ## Example Usage ```terraform resource "routeros_disk_settings" "test" { auto_smb_sharing = false auto_smb_user = "guest" auto_media_sharing = false auto_media_interface = "lo" } ``` ## Schema ### Optional - `auto_media_interface` (String) Interface that will be used in dynamic instance for ip/media when new disk/partition item is added in '/disk'. - `auto_media_sharing` (Boolean) Enables media dynamically when new disk/partition item is added in '/disk'. - `auto_smb_sharing` (Boolean) Enables dynamic SMB shares when new disk/partition item is added in '/disk'. - `auto_smb_user` (String) Default value for smb-sharing/smb-user setting, when new disk/partition item is added in '/disk'. - `default_mount_point_template` (String) Sets the default mount point template for each item added in `/disk`. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_disk_settings.test . ``` ================================================ FILE: docs/resources/dns.md ================================================ # routeros_dns (Resource) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_ip_dns](ip_dns.md) ================================================ FILE: docs/resources/dns_record.md ================================================ # routeros_dns_record (Resource) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_ip_dns_record](ip_dns_record.md) ================================================ FILE: docs/resources/file.md ================================================ # routeros_file (Resource) ## Example Usage ```terraform resource "routeros_file" "test" { name = "test" contents = "This is a test" } ``` ## Schema ### Required - `name` (String) Name of the file ### Optional - `contents` (String) The actual content of the file ### Read-Only - `creation_time` (String) A time when the file was created - `id` (String) The ID of this resource. - `last_modified` (String) A time when the file was modified - `package_architecture` (String) Architecture that package is built for. Applies only to RouterOS ".npk" files - `package_built_time` (String) A time when the package was built. Applies only to RouterOS ".npk" files - `package_name` (String) Name of the installable package. Applies only to RouterOS ".npk" files - `package_version` (String) A version of the installable package. Applies only to RouterOS ".npk" files - `size` (Number) File size in bytes - `type` (String) Type of the file. For folders, the file type is the directory ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/file get [print show-ids]] terraform import routeros_file.test "*1" #Or you can import a resource using one of its attributes terraform import routeros_file.test "name=xxx" ``` ================================================ FILE: docs/resources/firewall_addr_list.md ================================================ # routeros_firewall_addr_list (Resource) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_ip_firewall_addr_list](ip_firewall_addr_list.md) ================================================ FILE: docs/resources/firewall_filter.md ================================================ # routeros_firewall_filter (Resource) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_ip_firewall_filter](ip_firewall_filter.md) ================================================ FILE: docs/resources/firewall_mangle.md ================================================ # routeros_firewall_mangle (Resource) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_ip_firewall_mangle](ip_firewall_mangle.md) ================================================ FILE: docs/resources/firewall_nat.md ================================================ # routeros_firewall_nat (Resource) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_ip_firewall_nat](ip_firewall_nat.md) ================================================ FILE: docs/resources/gre.md ================================================ # routeros_gre (Resource) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_interface_gre](interface_gre.md) ================================================ FILE: docs/resources/identity.md ================================================ # routeros_identity (Resource) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_system_identity](system_identity.md) ================================================ FILE: docs/resources/interface_6to4.md ================================================ # routeros_interface_6to4 (Resource) ## Example Usage ```terraform resource "routeros_interface_6to4" "test" { name = "6to4-tunnel1" keepalive = "10,10" } ``` ## Schema ### Required - `name` (String) Interface name. ### Optional - `clamp_tcp_mss` (Boolean) Controls whether to change MSS size for received TCP SYN packets. When enabled, a router will change the MSS size for received TCP SYN packets if the current MSS size exceeds the tunnel interface MTU (taking into account the TCP/IP overhead). The received encapsulated packet will still contain the original MSS, and only after decapsulation the MSS is changed. - `comment` (String) - `disabled` (Boolean) - `dont_fragment` (String) - `dscp` (String) Set dscp value in GRE header to a fixed value '0..63' or 'inherit' from dscp value taken from tunnelled traffic. - `ipsec_secret` (String, Sensitive) When secret is specified, router adds dynamic IPsec peer to remote-address with pre-shared key and policy (by default phase2 uses sha1/aes128cbc). - `keepalive` (String) Tunnel keepalive parameter sets the time interval in which the tunnel running flag will remain even if the remote end of tunnel goes down. If configured time,retries fail, interface running flag is removed. Parameters are written in following format: `KeepaliveInterval,KeepaliveRetries` where `KeepaliveInterval` is time interval and `KeepaliveRetries` - number of retry attempts. `KeepaliveInterval` is integer 0..4294967295 - `local_address` (String) Source address of the tunnel packets, local on the router. - `mtu` (String) Layer3 Maximum transmission unit ('auto', 0 .. 65535). Look for the exact minimum value in the MikroTik documentation - `remote_address` (String) IP address of the remote end of the tunnel. ### Read-Only - `actual_mtu` (Number) - `id` (String) The ID of this resource. - `running` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/6to4 get [print show-ids]] terraform import routeros_interface_6to4.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_6to4.test "name=6to4-tunnel1" ``` ================================================ FILE: docs/resources/interface_bonding.md ================================================ # routeros_interface_bonding (Resource) ## Example Usage ```terraform resource "routeros_interface_bonding" "test" { name = "bonding-test" slaves = ["ether3", "ether4"] } ``` ## Schema ### Required - `name` (String) Name of the bonding interface. - `slaves` (Set of String) At least two ethernet-like interfaces separated by a comma, which will be used for bonding ### Optional - `arp` (String) Address Resolution Protocol for the interface. disabled - the interface will not use ARP enabled - the interface will use ARP proxy-arp - the interface will use the ARP proxy feature reply-only -the interface will only reply to requests originated from matching IPaddress/MAC address combinations which are entered as static entries inthe '/ip arp' table. No dynamic entries will be automatically stored inthe '/ip arp' table. Therefore for communications to be successful, avalid static entry must already exist. - `arp_interval` (String) Time in milliseconds defines how often to monitor ARP requests. - `arp_ip_targets` (String) IP target address which will be monitored if link-monitoring is set to arp. You can specify multiple IP addresses, separated by a comma. - `arp_timeout` (String) ARP timeout is time how long ARP record is kept in ARP table after no packets are received from IP. Value auto equals to the value of arp-timeout in IP/Settings, default is 30s. Can use postfix `ms`, `s`, `m`, `h`, `d` for milliseconds, seconds, minutes, hours or days. If no postfix is set then seconds (s) is used. - `comment` (String) - `disabled` (Boolean) - `down_delay` (String) If a link failure has been detected, the bonding interface is disabled for a down-delay time. The value should be a multiple of mii-interval, otherwise, it will be rounded down to the nearest value. This property only has an effect when link-monitoring is set to mii. - `forced_mac_address` (String) Bydefault, the bonding interface will use the MAC address of the firstselected slave interface. This property allows to configure static MACaddress for the bond interface (all zeros, broadcast or multicastaddresses will not apply). RouterOS will automatically change the MACaddress for slave interfaces and it will be visible in /interface ethernet configuration export. - `lacp_mode` (String) Specifies whether ports actively or passively participates in the LACP: - **active** - ports actively initiate LACP communication, regardless of the partner's LACP mode (i.e, it "speaks" even if the partner is silent) - **passive** - ports only respond to LACP messages and do not initiate them unless the partner is in active mode (i.e., it "listens" and responds only if spoken to). - `lacp_rate` (String) LinkAggregation Control Protocol rate specifies how often to exchange withLACPDUs between bonding peers. Used to determine whether a link is up orother changes have occurred in the network. LACP tries to adapt tothese changes providing failover. - `lacp_user_key` (Number) Specifiesthe upper 10 bits of the port key. The lower 6 bits are automaticallyassigned based on individual port link speed and duplex. The setting isavailable only since RouterOS v7.3. - `link_monitoring` (String) Method to use for monitoring the link (whether it is up or down) arp - uses Address Resolution Protocol to determine whether the remote interface is reachable mii - uses Media Independent Interface to determine link status. Link status determination relies on the device driver. none - no method for link monitoring is used. Note: some bonding modes require specific link monitoring to work properly. - `mii_interval` (String) How often to monitor the link for failures (the parameter used only if link-monitoring is mii) - `min_links` (Number) How many active slave links needed for bonding to become active. - `mlag_id` (Number) ChangesMLAG ID for bonding interface. The same MLAG ID should be used on bothpeer devices to successfully create a single MLAG. See more details on MLAG . - `mode` (String) Specifies one of the bonding policies: * 802.3ad -IEEE 802.3ad dynamic link aggregation. In this mode, the interfaces areaggregated in a group where each slave shares the same speed. Itprovides fault tolerance and load balancing. Slave selection foroutgoing traffic is done according to the transmit-hash-policy * active-backup - provides link backup. Only one slave can be active at a time. Another slave only becomes active, if the first one fails. * balance-alb - adaptive load balancing. The same as balance-tlb but received traffic is also balanced. The device driver should have support for changing it's MAC address. * balance-rr -round-robin load balancing. Slaves in a bonding interface will transmitand receive data in sequential order. It provides load balancing andfault tolerance. * balance-tlb -Outgoing traffic is distributed according to the current load on eachslave. Incoming traffic is not balanced and is received by the currentslave. If receiving slave fails, then another slave takes the MACaddress of the failed slave. * balance-xor - Transmit based on the selected transmit-hash-policy. This mode provides load balancing and fault tolerance. * broadcast -Broadcasts the same data on all interfaces at once. This provides faulttolerance but slows down traffic throughput on some slow machines. - `mtu` (Number) MaximumTransmit Unit in bytes. Must be smaller or equal to the smallest L2MTUvalue of a bonding slave. L2MTU of a bonding interface is determined bythe lowest L2MTU value among its slave interfaces. - `primary` (String) Controlsthe primary interface between active slave ports, works only foractive-backup, balance-tlb and balance-alb modes. For active-backupmode, it controls which running interface is supposed to send andreceive the traffic. For balance-tlb mode, it controls which runninginterface is supposed to receive all the traffic, but for balance-albmode, it controls which interface is supposed to receive the unbalanced traffic (the non-IPv4 traffic). When none of the interfaces are selectedas primary, device will automatically select the interface that isconfigured as the first one. - `transmit_hash_policy` (String) Selects the transmit hash policy to use for slave selection in balance-xor and 802.3ad modes: * layer-2 -Uses XOR of hardware MAC addresses to generate the hash. This algorithm will place all traffic to a particular network peer on the same slave.This algorithm is 802.3ad compliant. * layer-2-and-3 -This policy uses a combination of layer2 and layer3 protocolinformation to generate the hash. Uses XOR of hardware MAC addresses andIP addresses to generate the hash. This algorithm will place alltraffic to a particular network peer on the same slave. For non-IPtraffic, the formula is the same as for the layer2 transmit hash policy.This policy is intended to provide a more balanced distribution oftraffic than layer2 alone, especially in environments where a layer3gateway device is required to reach most destinations. This algorithm is802.3ad compliant. * layer-3-and-4 - This policyuses upper layer protocol information, when available, to generate thehash. This allows for traffic to a particular network peer to spanmultiple slaves, although a single connection will not span multipleslaves. For fragmented TCP or UDP packets and all other IP protocoltraffic, the source and destination port information is omitted. Fornon-IP traffic, the formula is the same as for the layer2 transmit hashpolicy. This algorithm is not fully 802.3ad compliant. - `up_delay` (String) If a link has been brought up, the bonding interface is disabled for up-delay time and after this time it is enabled. The value should be a multiple of mii-interval , otherwise, it will be rounded down to the nearest value. This property only has an effect when link-monitoring is set to mii. ### Read-Only - `id` (String) The ID of this resource. - `mac_address` (String) Current mac address. - `running` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/bonding get [print show-ids]] terraform import routeros_interface_bonding.test "*0" ``` ================================================ FILE: docs/resources/interface_bridge.md ================================================ # routeros_interface_bridge (Resource) ## Example Usage ```terraform resource "routeros_interface_bridge" "bridge" { name = "bridge" vlan_filtering = true } ``` ## Schema ### Required - `name` (String) Changing the name of this resource will force it to be recreated. > The links of other configuration properties to this resource may be lost! > Changing the name of the resource outside of a Terraform will result in a loss of control integrity for that resource! ### Optional - `add_dhcp_option82` (Boolean) Whether to add DHCP Option-82 information (Agent Remote ID and Agent Circuit ID) to DHCP packets. Can be used together with Option-82 capable DHCP server to assign IP addresses and implement policies. This property only has effect when dhcp-snooping is set to yes. - `admin_mac` (String) Static MAC address of the bridge. This property only has effect when auto-mac is set to no. - `ageing_time` (String) How long a host's information will be kept in the bridge database. - `arp` (String) Address Resolution Protocol mode: * disabled - the interface will not use ARP * enabled - the interface will use ARP * local-proxy-arp - the router performs proxy ARP on the interface and sends replies to the same interface * proxy-arp - the router performs proxy ARP on the interface and sends replies to other interfaces * reply-only - the interface will only reply to requests originated from matching IP address/MAC address combinations which are entered as static entries in the ARP table. No dynamic entries will be automatically stored in the ARP table. Therefore for communications to be successful, a valid static entry must already exist. - `arp_timeout` (String) ARP timeout is time how long ARP record is kept in ARP table after no packets are received from IP. Value auto equals to the value of arp-timeout in IP/Settings, default is 30s. Can use postfix `ms`, `s`, `m`, `h`, `d` for milliseconds, seconds, minutes, hours or days. If no postfix is set then seconds (s) is used. - `auto_mac` (Boolean) Automatically select one MAC address of bridge ports as a bridge MAC address, bridge MAC will be chosen from the first added bridge port. After a device reboot, the bridge MAC can change depending on the port-number. - `comment` (String) - `dhcp_snooping` (Boolean) - `disabled` (Boolean) - `ether_type` (String) This property only has effect when vlan-filtering is set to yes. - `fast_forward` (Boolean) - `forward_delay` (String) Time which is spent during the initialization phase of the bridge interface (i.e., after router startup or enabling the interface) in listening/learning state before the bridge will start functioning normally. - `forward_reserved_addresses` (Boolean) An option whether to forward IEEE reserved multicast MAC addresses that are in the `01:80:C2:00:00:0x` range. This option is available in RouterOS starting from version 7.16. - `frame_types` (String) Specifies allowed frame types on a bridge port. This property only has effect when vlan-filtering is set to yes. - `igmp_snooping` (Boolean) Enables multicast group and port learning to prevent multicast traffic from flooding all interfaces in a bridge. - `igmp_version` (Number) Selects the IGMP version in which IGMP general membership queries will be generated. This property only has effect when igmp-snooping is set to yes. - `ingress_filtering` (Boolean) Enables or disables VLAN ingress filtering, which checks if the ingress port is a member of the received VLAN ID in the bridge VLAN table. Should be used with frame-types to specify if the ingress traffic should be tagged or untagged. This property only has effect when vlan-filtering is set to yes. - `last_member_interval` (String) If a port has fast-leave set to no and a bridge port receives a IGMP Leave message, then a IGMP Snooping enabled bridge will send a IGMP query to make sure that no devices has subscribed to a certain multicast stream on a bridge port. - `last_member_query_count` (Number) How many times should last-member-interval pass until a IGMP Snooping bridge will stop forwarding a certain multicast stream. This property only has effect when igmp-snooping is set to yes. - `max_hops` (Number) Bridge count which BPDU can pass in a MSTP enabled network in the same region before BPDU is being ignored. This property only has effect when protocol-mode is set to mstp. - `max_learned_entries` (String) An option to set the maximum number of learned hosts for the bridge interface. This option is available in RouterOS starting from version 7.16. - `max_message_age` (String) Changes the Max Age value in BPDU packets, which is transmitted by the root bridge. This property only has effect when protocol-mode is set to stp or rstp. Value: 6s..40s - `membership_interval` (String) Amount of time after an entry in the Multicast Database (MDB) is removed if a IGMP membership report is not received on a certain port. This property only has effect when igmp-snooping is set to yes. - `mld_version` (Number) Selects the MLD version. Version 2 adds support for source-specific multicast. This property only has effect when RouterOS IPv6 package is enabled and igmp-snooping is set to yes. - `mtu` (String) The default bridge MTU value without any bridge ports added is 1500. The MTU value can be set manually, but it cannot exceed the bridge L2MTU or the lowest bridge port L2MTU. If a new bridge port is added with L2MTU which is smaller than the actual-mtu of the bridge (set by the mtu property), then manually set value will be ignored and the bridge will act as if mtu=auto is set. - `multicast_querier` (Boolean) Multicast querier generates IGMP general membership queries to which all IGMP capable devices respond with an IGMP membership report, usually a PIM (multicast) router or IGMP proxy generates these queries. This property only has an effect when igmp-snooping is set to yes. Additionally, the igmp-snooping should be disabled/enabled after changing multicast-querier property. - `multicast_router` (String) A multicast router port is a port where a multicast router or querier is connected. On this port, unregistered multicast streams and IGMP/MLD membership reports will be sent. This setting changes the state of the multicast router for a bridge interface itself. This property can be used to send IGMP/MLD membership reports and multicast traffic to the bridge interface for further multicast routing or proxying. This property only has an effect when igmp-snooping is set to yes. - `mvrp` (Boolean) Enables MVRP for bridge (available since RouterOS 7.15). It ensures that the MAC address 01:80:C2:00:00:21 is trapped and not forwarded, the vlan-filtering must be enabled. - `port_cost_mode` (String) An option that changes the port path cost and internal path cost mode for bridged ports, utilizing automatic values based on interface speed. - `priority` (String) Bridge priority, used by STP to determine root bridge, used by MSTP to determine CIST and IST regional root bridge. This property has no effect when protocol-mode is set to none. - `protocol_mode` (String) Select Spanning tree protocol (STP) or Rapid spanning tree protocol (RSTP) to ensure a loop-free topology for any bridged LAN. - `pvid` (Number) Port VLAN ID (pvid) specifies which VLAN the untagged ingress traffic is assigned to. It applies e.g. to frames sent from bridge IP and destined to a bridge port. This property only has effect when vlan-filtering is set to yes. - `querier_interval` (String) Used to change the interval how often a bridge checks if it is the active multicast querier. This property only has effect when igmp-snooping and multicast-querier is set to yes. - `query_interval` (String) Used to change the interval how often IGMP general membership queries are sent out. This property only has effect when igmp-snooping and multicast-querier is set to yes. - `query_response_interval` (String) Interval in which a IGMP capable device must reply to a IGMP query with a IGMP membership report. This property only has effect when igmp-snooping and multicast-querier is set to yes. - `region_name` (String) MSTP region name. This property only has effect when protocol-mode is set to mstp. - `region_revision` (Number) MSTP configuration revision number. This property only has effect when protocol-mode is set to mstp. - `startup_query_count` (Number) Specifies how many times must startup-query-interval pass until the bridge starts sending out IGMP general membership queries periodically. This property only has effect when igmp-snooping and multicast-querier is set to yes. - `startup_query_interval` (String) Used to change the amount of time after a bridge starts sending out IGMP general membership queries after the bridge is enabled. This property only has effect when igmp-snooping and multicast-querier is set to yes. - `transmit_hold_count` (Number) The Transmit Hold Count used by the Port Transmit state machine to limit transmission rate. - `vlan_filtering` (Boolean) Globally enables or disables VLAN functionality for bridge. ### Read-Only - `actual_mtu` (Number) - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. - `l2mtu` (Number) Layer2 Maximum transmission unit. [See](https://wiki.mikrotik.com/wiki/Maximum_Transmission_Unit_on_RouterBoards). - `mac_address` (String) Current mac address. - `running` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/bridge get [print show-ids]] terraform import routeros_interface_bridge.bridge "*1" ``` ================================================ FILE: docs/resources/interface_bridge_filter.md ================================================ # routeros_interface_bridge_filter (Resource) ## Example Usage ```terraform variable "bridge_filter_rule" { type = list(object({ chain = string action = string connection_state = optional(string) in_interface_list = optional(string, "all") out_interface_list = optional(string) src_address = optional(string) dst_address = optional(string) src_port = optional(string) dst_port = optional(string) jump_target = optional(string) protocol = optional(string) comment = optional(string, "(terraform-defined)") log = optional(bool, false) log_prefix = optional(string, "") disabled = optional(bool, false) })) default = [ { "action" = "drop", "chain" = "forward", "comment" = "Drop data between bridge ports" }, { "action" = "drop", "chain" = "forward", "comment" = "Block VLAN encap", "log_prefix" = "Block VLAN encap", "mac_protocol" = "vlan" }, { "action" = "accept", "chain" = "forward", "comment" = "", "disabled" = "true", "dst_address" = "224.0.0.251/32", "ip_protocol" = "udp", "log_prefix" = "Allow bonjour", "mac_protocol" = "ip" }, ] } locals { rule_map = { for idx, rule in var.bridge_filter_rule : format("%03d", idx) => rule } } resource "routeros_interface_bridge_filter" "rules" { for_each = local.rule_map chain = each.value.chain action = each.value.action comment = each.value.comment log = each.value.log log_prefix = each.value.log_prefix disabled = each.value.disabled connection_state = each.value.connection_state in_interface_list = each.value.in_interface_list dst_port = each.value.dst_port protocol = each.value.protocol src_address = each.value.src_address jump_target = each.value.jump_target } resource "routeros_move_items" "bridge_filter_rules" { # resource_name = "routeros_interface_bridge_filter" resource_path = "/interface/bridge/filter" sequence = [for i, _ in local.rule_map : routeros_interface_bridge_filter.rules[i].id] depends_on = [routeros_interface_bridge_filter.rules] } ``` ## Schema ### Required - `action` (String) Action to take if a packet is matched by the rule - `chain` (String) Specifies to which chain rule will be added. If the input does not match the name of an already defined chain, a new chain will be created. ### Optional - `arp_dst_mac_address` (String) ARP destination MAC address - `arp_gratuitous` (Boolean) Matches ARP gratuitous packets. - `arp_hardware_type` (Number) ARP hardware type. This is normally Ethernet (Type 1). - `arp_opcode` (String) Action to take if a packet is matched by the rule - `arp_packet_type` (Number) ARP Packet Type - `arp_src_address` (String) ARP source IP address. - `arp_src_mac_address` (String) ARP source MAC address. - `comment` (String) - `disabled` (Boolean) - `dst_address` (String) Destination IP address (only if MAC protocol is set to IP). - `dst_mac_address` (String) Destination MAC address. - `dst_port` (String) List of destination port numbers or port number ranges. - `in_bridge` (String) Bridge interface through which the packet is coming in. - `in_bridge_list` (String) Set of bridge interfaces defined in interface list. Works the same as in-bridge. - `in_interface` (String) Physical interface (i.e., bridge port) through which the packet is coming in. - `in_interface_list` (String) Set of interfaces defined in interface list. Works the same as in-interface. - `ingress_priority` (Number) Integer. Matches the priority of an ingress packet. Priority may be derived from VLAN, WMM, DSCP,or MPLS EXP bit. - `ip_protocol` (String) IP protocol (only if MAC protocol is set to IPv4) - `jump_target` (String) Name of the target chain to jump to. Applicable only if action=jump. - `limit` (String) Matches packets up to a limited rate (packet rate or bit rate). A rule using this matcher will match until this limit is reached. Parameters are written in the following format: rate[/time],burst:mode. - `log` (Boolean) Add a message to the system log. - `log_prefix` (String) Adds specified text at the beginning of every log message. Applicable if action=log or log=yes configured. - `mac_protocol` (String) Ethernet payload type (MAC-level protocol). To match protocol type for VLAN encapsulated frames (0x8100 or 0x88a8), a vlan-encap property should be used. - `new_packet_mark` (String) Sets a new packet-mark value. - `new_priority` (Number) Sets a new priority for a packet. This can be the VLAN, WMM or MPLS EXP priority - `out_bridge` (String) Bridge interface through which the packet going out. - `out_bridge_list` (String) Set of bridge interfaces defined in interface list. Works the same as out-bridge. - `out_interface` (String) Interface the packet has entered the router. - `out_interface_list` (String) Set of interfaces defined in interface list. Works the same as out-interface. - `packet_mark` (String) Match packets with a certain packet mark. - `packet_type` (String) Match packets with a certain packet mark. - `passthrough` (Boolean) Whether to let the packet to pass further (like action passthrough) into the filter or not (property only valid some actions). - `place_before` (String) Before which position the rule will be inserted. > Please check the effect of this option, as it does not work as you think! > Best way to use in conjunction with a data source. See [example](../data-sources/ip_firewall.md#example-usage). - `src_address` (String) Source port number or range (only for TCP or UDP protocols). - `src_mac_address` (String) Source MAC address. - `src_port` (String) List of source port numbers or port number ranges. - `stp_flags` (String) Match packets with a certain packet mark. - `stp_forward_delay` (Number) Forward delay timer. - `stp_hello_time` (Number) STP hello packets time. - `stp_max_age` (Number) Maximal STP message age. - `stp_port` (Number) STP port identifier. - `stp_root_address` (String) Root bridge MAC address - `stp_root_cost` (Number) Root bridge cost. - `stp_root_priority` (Number) - `stp_sender_address` (String) STP message sender MAC address. - `stp_sender_priority` (Number) STP sender priority. - `stp_type` (String) The BPDU type: config - configuration BPDU OR tcn - topology change notification - `tls_host` (String) Allows matching https traffic based on TLS SNI hostname. Accepts GLOB syntax for wildcard matching - `vlan_encap` (Number) Matches the MAC protocol type encapsulated in the VLAN frame. - `vlan_id` (Number) Matches the VLAN identifier field. - `vlan_priority` (Number) Matches the VLAN identifier field. ### Read-Only - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/bridge/filter get [print show-ids]] terraform import routeros_interface_bridge_filter.rule "*0" #Or you can import a resource using one of its attributes terraform import routeros_interface_bridge_filter.rule "dst_address=224.0.0.251/32" ``` ================================================ FILE: docs/resources/interface_bridge_port.md ================================================ # routeros_interface_bridge_port (Resource) ## Example Usage ```terraform resource "routeros_interface_bridge_port" "bridge_port" { bridge = "bridge" interface = "ether5" pvid = "50" } ``` ## Schema ### Required - `bridge` (String) - `interface` (String) Name of the interface. ### Optional - `auto_isolate` (Boolean) When enabled, prevents a port moving from discarding into forwarding state if no BPDUs are received from the neighboring bridge. The port will change into a forwarding state only when a BPDU is received. This property only has an effect when protocol-mode is set to rstp or mstp and edge is set to no. - `bpdu_guard` (Boolean) This property has no effect when protocol-mode is set to none. - `broadcast_flood` (Boolean) When enabled, bridge floods broadcast traffic to all bridge egress ports. When disabled, drops broadcast traffic on egress ports. - `comment` (String) - `disabled` (Boolean) - `edge` (String) Set port as edge port or non-edge port, or enable edge discovery. Edge ports are connected to a LAN that has no other bridges attached. - `fast_leave` (Boolean) Enables IGMP Fast leave feature on the port. - `frame_types` (String) Specifies allowed ingress frame types on a bridge port. This property only has effect when vlan-filtering is set to yes. - `horizon` (String) Use split horizon bridging to prevent bridging loops. Set the same value for group of ports, to prevent them from sending data to ports with the same horizon value. Split horizon is a software feature that disables hardware offloading. This value is integer '0'..'429496729' or 'none'. - `hw` (Boolean) Enable or disable Hardware Offloading of the interface. - `ingress_filtering` (Boolean) Enables or disables VLAN ingress filtering, which checks if the ingress port is a member of the received VLAN ID in the bridge VLAN table. Should be used with frame-types to specify if the ingress traffic should be tagged or untagged. This property only has effect when vlan-filtering is set to yes. - `internal_path_cost` (Number) Path cost to the interface for MSTI0 inside a region. This property only has effect when protocol-mode is set to mstp. - `learn` (String) Changes MAC learning behaviour on a bridge port - `multicast_router` (String) Changes the state of a bridge port whether IGMP membership reports are going to be forwarded to this port. - `mvrp_applicant_state` (String) MVRP applicant options (available since RouterOS 7.15): - non-participant - port does not send any MRP messages; - normal-participant - port participates normally in MRP exchanges. - `mvrp_registrar_state` (String) MVRP registrar options (available since RouterOS 7.15): - fixed - port ignores all MRP messages, and remains Registered (IN) in all configured vlans. - normal - port receives MRP messages and handles them according to the standard. - `path_cost` (String) Path cost to the interface, used by STP to determine the "best" path, used by MSTP todetermine "best" path between regions. This property has no effect when protocol-mode is set to none. - `point_to_point` (String) Specifies if a bridge port is connected to a bridge using a point-to-point link for faster convergence in case of failure. This property has no effect when protocol-mode is set to none. - `priority` (String) The priority of the interface, used by STP to determine the root port, used by MSTP to determine root port between regions. - `pvid` (Number) ort VLAN ID (pvid) specifies which VLAN the untagged ingress traffic is assigned to. This property only has effect when vlan-filtering is set to yes. - `restricted_role` (Boolean) Enable the restricted role on a port, used by STP to forbid a port becoming a root port. This property only has effect when protocol-mode is set to mstp. - `restricted_tcn` (Boolean) Disable topology change notification (TCN) sending on a port, used by STP to forbid network topology changes to propagate. This property only has effect when protocol-mode is set to mstp. - `tag_stacking` (Boolean) Forces all packets to be treated as untagged packets. Packets on ingress port will be tagged with another VLAN tag regardless if a VLAN tag already exists, packets will be tagged with a VLAN ID that matches the pvid value and will use EtherType that is specified in ether-type. This property only has effect when vlan-filtering is set to yes. - `trusted` (Boolean) When enabled, it allows to forward DHCP packets towards DHCP server through this port. Mainly used to limit unauthorized servers to provide malicious information for users. This property only has effect when dhcp-snooping is set to yes. - `unknown_multicast_flood` (Boolean) When enabled, bridge floods unknown multicast traffic to all bridge egress ports. - `unknown_unicast_flood` (Boolean) When enabled, bridge floods unknown unicast traffic to all bridge egress ports. ### Read-Only - `designated_bridge` (String) Root bridge ID (bridge priority and the bridge MAC address). - `designated_bridge_id` (String) Shows the designated bridge identifier, as determined from the port's priority vector. - `designated_cost` (String) Designated cost. - `designated_port_id` (String) Shows the designated port identifier, as determined from the port's priority vector. - `designated_port_number` (Number) Designated port number. - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `edge_port` (Boolean) Whether port is an edge port or not. - `edge_port_discovery` (Boolean) Whether port is set to automatically detect edge ports. - `external_fdb_status` (Boolean) Whether registration table is used instead of forwarding data base. - `forwarding` (Boolean) Shows if the port is not blocked by (R/M)STP. - `hw_offload` (Boolean) Hardware offloading state. - `hw_offload_group` (String) Switch chip used by the port. - `id` (String) The ID of this resource. - `inactive` (Boolean) - `last_topology_change` (String) Last topology change timer, records time since the last change. - `learning` (Boolean) Shows whether the port is capable of learning MAC addresses. - `nextid` (String) - `point_to_point_port` (Boolean) Whether the port is connected to a bridge port using full-duplex (true) or half-duplex (false). - `port_id` (String) In Spanning Tree Protocol each port has a unique Port Identifier. Priority[hex] + port number. - `role` (String) (R/M)STP algorithm assigned role of the port - `root_path_cost` (Number) The total cost of the path to the root-bridge. - `sending_rstp` (String) Whether the port is sending RSTP or MSTP BPDU types. A port will transit to STP type when RSTP/MSTP enabled port receives a STP BPDU - `status` (String) Port status ('in-bridge' - port is enabled). ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/bridge/port get [print show-ids]] terraform import routeros_interface_bridge_port.bridge_port "*0" #Or you can import a resource using one of its attributes terraform import routeros_interface_bridge_port.bridge_port "name=xxx" ``` ================================================ FILE: docs/resources/interface_bridge_settings.md ================================================ # routeros_interface_bridge_settings (Resource) ## Example Usage ```terraform resource "routeros_interface_bridge_settings" "settings" { use_ip_firewall = true } ``` ## Schema ### Optional - `allow_fast_path` (Boolean) Whether to enable a bridge FastPath globally. - `use_ip_firewall` (Boolean) Force bridged traffic to also be processed by prerouting, forward and postrouting sections of IP routing ( Packet Flow). This does not apply to routed traffic. This property is required in case you want to assign Simple Queues or global Queue Tree to traffic in a bridge. Property use-ip-firewall-for-vlan is required in case bridge vlan-filtering is used. - `use_ip_firewall_for_pppoe` (Boolean) Send bridged un-encrypted PPPoE traffic to also be processed by IP/Firewall. This property only has effect when use-ip-firewall is set to yes. This property is required in case you want to assign Simple Queues or global Queue Tree to PPPoE traffic in a bridge. - `use_ip_firewall_for_vlan` (Boolean) Send bridged VLAN traffic to also be processed by IP/Firewall. This property only has effect when use-ip-firewall is set to yes. This property is required in case you want to assign Simple Queues or global Queue Tree to VLAN traffic in a bridge. ### Read-Only - `bridge_fast_forward_bytes` (Number) Shows byte count forwarded by Bridge Fast Forward. - `bridge_fast_forward_packets` (Number) Shows packet count forwarded by Bridge Fast Forward. - `bridge_fast_path_active` (Boolean) Shows whether a bridge FastPath is active globally, FastPatch status per bridge interface is not displayed. - `bridge_fast_path_bytes` (Number) Shows byte count forwarded by Bridge Fast Path. - `bridge_fast_path_packets` (Number) Shows packet count forwarded by Bridge FastPath. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_interface_bridge_settings.settings . ``` ================================================ FILE: docs/resources/interface_bridge_vlan.md ================================================ # routeros_interface_bridge_vlan (Resource) ## Example Usage ```terraform resource "routeros_interface_bridge_vlan" "bridge_vlan" { bridge = "bridge" vlan_ids = ["50"] tagged = [ "bridge", "ether1" ] untagged = [ "ether5" ] } resource "routeros_interface_bridge_vlan" "bridge_vlan" { bridge = "bridge" vlan_ids = ["4", "10", "20", "50", "100", "101", "102", "103", "112", "201", "202", "220", "254"] } resource "routeros_interface_bridge_vlan" "bridge_vlan" { bridge = "bridge" vlan_ids = ["4", "10", "20", "50", "100-103", "112", "201", "202", "220", "254"] } resource "routeros_interface_bridge_vlan" "bridge_vlan" { bridge = "bridge" vlan_ids = ["100-115", "120", "122", "128-130"] } ``` ## Schema ### Required - `bridge` (String) The bridge interface which the respective VLAN entry is intended for. - `vlan_ids` (Set of String) The list of VLAN IDs for certain port configuration. This setting accepts VLAN ID range as well as comma separated values. E.g. `vlan-ids=["100-115","120","122","128-130"]`. ### Optional - `comment` (String) - `disabled` (Boolean) - `mvrp_forbidden` (List of String) Ports that ignore all MRP messages and remains Not Registered (MT), as well as disables applicant from declaring specific VLAN ID (available since RouterOS 7.15). - `tagged` (Set of String) Interface list with a VLAN tag adding action in egress. This setting accepts comma separated values. E.g. tagged=ether1,ether2. - `untagged` (Set of String) Interface list with a VLAN tag removing action in egress. This setting accepts comma separated values. E.g. untagged=ether3,ether4 ### Read-Only - `current_tagged` (List of String) - `current_untagged` (List of String) - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/bridge/vlan get [print show-ids]] terraform import routeros_interface_bridge_vlan.bridge_vlan "*0" #Or you can import a resource using one of its attributes terraform import routeros_interface_bridge_vlan.bridge_vlan "name=xxx" ``` ================================================ FILE: docs/resources/interface_detect_internet.md ================================================ # routeros_interface_detect_internet (Resource) ## Example Usage ```terraform resource "routeros_interface_detect_internet" "test" { internet_interface_list = "all" wan_interface_list = "all" lan_interface_list = "all" detect_interface_list = "all" } ``` ## Schema ### Optional - `detect_interface_list` (String) All interfaces in the list will be monitored by Detect Internet. - `internet_interface_list` (String) Interfaces with state Internet will be dynamically added to this list. - `lan_interface_list` (String) Interfaces with state Lan will be dynamically added to this list. - `wan_interface_list` (String) Interfaces with state Wan will be dynamically added to this list. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_interface_detect_internet.test . ``` ================================================ FILE: docs/resources/interface_dot1x_client.md ================================================ # routeros_interface_dot1x_client (Resource) ## Example Usage ```terraform resource "routeros_interface_dot1x_client" "ether2" { eap_methods = ["eap-peap", "eap-mschapv2"] identity = "router" interface = "ether2" } ``` ## Schema ### Required - `eap_methods` (Set of String) A set of EAP methods used for authentication: `eap-tls`, `eap-ttls`, `eap-peap`, `eap-mschapv2`. - `identity` (String) The supplicant identity that is used for EAP authentication. - `interface` (String) Name of the interface. ### Optional - `anon_identity` (String) Identity for outer layer EAP authentication. Used only with `eap-ttls` and `eap-peap` methods. If not set, the value from the identity parameter will be used for outer layer EAP authentication. - `certificate` (String) Name of a certificate. Required when the `eap-tls` method is used. - `comment` (String) - `disabled` (Boolean) - `password` (String, Sensitive) Cleartext password for the supplicant. ### Read-Only - `id` (String) The ID of this resource. - `status` (String) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/dot1x/client get [print show-ids]] terraform import routeros_interface_dot1x_client.ether2 *1 #Or you can import a resource using one of its attributes terraform import routeros_interface_dot1x_client.ether2 "name=xxx" ``` ================================================ FILE: docs/resources/interface_dot1x_server.md ================================================ # routeros_interface_dot1x_server (Resource) ## Example Usage ```terraform resource "routeros_interface_dot1x_server" "ether2" { auth_types = ["mac-auth"] interface = "ether2" } ``` ## Schema ### Required - `interface` (String) Name of the interface. ### Optional - `accounting` (Boolean) Whether to send RADIUS accounting requests to the authentication server. - `auth_timeout` (String) Total time available for EAP authentication. - `auth_types` (Set of String) Used authentication type on a server interface. Comma-separated list of `dot1x` and `mac-auth`. - `comment` (String) - `disabled` (Boolean) - `guest_vlan_id` (Number) Assigned VLAN when end devices do not support dot1x authentication and no mac-auth fallback is configured. - `interim_update` (String) Interval between scheduled RADIUS Interim-Update messages. - `mac_auth_mode` (String) An option that allows to control User-Name and User-Password RADIUS attributes when using MAC authentication. - `radius_mac_format` (String) An option that controls how the MAC address of the client is encoded in the User-Name and User-Password attributes when using MAC authentication. - `reauth_timeout` (String) An option that enables server port re-authentication. - `reject_vlan_id` (Number) Assigned VLAN when authentication failed, and a RADIUS server responded with an Access-Reject message. - `retrans_timeout` (String) The time interval between message re-transmissions if no response is received from the supplicant. - `server_fail_vlan_id` (Number) Assigned VLAN when RADIUS server is not responding and request timed out. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/dot1x/server get [print show-ids]] terraform import routeros_interface_dot1x_server.ether2 *1 #Or you can import a resource using one of its attributes terraform import routeros_interface_dot1x_server.ether2 "name=xxx" ``` ================================================ FILE: docs/resources/interface_eoip.md ================================================ # routeros_interface_eoip (Resource) ## Example Usage ```terraform resource "routeros_interface_eoip" "eoip_tunnel1" { name = "eoip-tunnel1" local_address = "192.168.88.1" remote_address = "192.168.88.2" disabled = true } ``` ## Schema ### Required - `name` (String) Changing the name of this resource will force it to be recreated. > The links of other configuration properties to this resource may be lost! > Changing the name of the resource outside of a Terraform will result in a loss of control integrity for that resource! ### Optional - `allow_fast_path` (Boolean) Whether to allow FastPath processing. Must be disabled if IPsec tunneling is used. - `arp` (String) Address Resolution Protocol mode: * disabled - the interface will not use ARP * enabled - the interface will use ARP * local-proxy-arp - the router performs proxy ARP on the interface and sends replies to the same interface * proxy-arp - the router performs proxy ARP on the interface and sends replies to other interfaces * reply-only - the interface will only reply to requests originated from matching IP address/MAC address combinations which are entered as static entries in the ARP table. No dynamic entries will be automatically stored in the ARP table. Therefore for communications to be successful, a valid static entry must already exist. - `arp_timeout` (String) ARP timeout is time how long ARP record is kept in ARP table after no packets are received from IP. Value auto equals to the value of arp-timeout in IP/Settings, default is 30s. Can use postfix `ms`, `s`, `m`, `h`, `d` for milliseconds, seconds, minutes, hours or days. If no postfix is set then seconds (s) is used. - `clamp_tcp_mss` (Boolean) Controls whether to change MSS size for received TCP SYN packets. When enabled, a router will change the MSS size for received TCP SYN packets if the current MSS size exceeds the tunnel interface MTU (taking into account the TCP/IP overhead). The received encapsulated packet will still contain the original MSS, and only after decapsulation the MSS is changed. - `comment` (String) - `disabled` (Boolean) - `dont_fragment` (String) - `dscp` (String) Set dscp value in GRE header to a fixed value '0..63' or 'inherit' from dscp value taken from tunnelled traffic. - `ipsec_secret` (String, Sensitive) When secret is specified, router adds dynamic IPsec peer to remote-address with pre-shared key and policy (by default phase2 uses sha1/aes128cbc). - `keepalive` (String) Tunnel keepalive parameter sets the time interval in which the tunnel running flag will remain even if the remote end of tunnel goes down. If configured time,retries fail, interface running flag is removed. Parameters are written in following format: `KeepaliveInterval,KeepaliveRetries` where `KeepaliveInterval` is time interval and `KeepaliveRetries` - number of retry attempts. `KeepaliveInterval` is integer 0..4294967295 - `local_address` (String) Source address of the tunnel packets, local on the router. - `loop_protect` (String) - `loop_protect_disable_time` (String) - `loop_protect_send_interval` (String) - `mtu` (String) Layer3 Maximum transmission unit ('auto', 0 .. 65535). Look for the exact minimum value in the MikroTik documentation - `remote_address` (String) IP address of the remote end of the tunnel. - `tunnel_id` (Number) Unique tunnel identifier, which must match the other side of the tunnel. ### Read-Only - `actual_mtu` (Number) - `id` (String) The ID of this resource. - `l2mtu` (Number) Layer2 Maximum transmission unit. [See](https://wiki.mikrotik.com/wiki/Maximum_Transmission_Unit_on_RouterBoards). - `loop_protect_status` (String) - `mac_address` (String) Current mac address. - `running` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/eoip get [print show-ids]] terraform import routeros_interface_eoip.eoip_tunnel1 *B #Or you can import a resource using one of its attributes terraform import routeros_interface_eoip.eoip_tunnel1 "name=xxx" ``` ================================================ FILE: docs/resources/interface_ethernet.md ================================================ # routeros_interface_ethernet (Resource) ## Example Usage ```terraform resource "routeros_interface_ethernet" "test" { factory_name = "sfp-sfpplus8" name = "swtich-eth0" mtu = 9000 } ``` ## Schema ### Required - `factory_name` (String) The factory name of the identifier, serves as resource identifier. Determines which interface will be updated. - `name` (String) Name of the ethernet interface. ### Optional - `advertise` (String) Advertised speed and duplex modes for Ethernet interfaces over twisted pair, only applies when auto-negotiation is enabled. Advertising higher speeds than the actual interface supported speed will have no effect, multiple options are allowed. - `arp` (String) Address Resolution Protocol mode: * disabled - the interface will not use ARP * enabled - the interface will use ARP * local-proxy-arp - the router performs proxy ARP on the interface and sends replies to the same interface * proxy-arp - the router performs proxy ARP on the interface and sends replies to other interfaces * reply-only - the interface will only reply to requests originated from matching IP address/MAC address combinations which are entered as static entries in the ARP table. No dynamic entries will be automatically stored in the ARP table. Therefore for communications to be successful, a valid static entry must already exist. - `arp_timeout` (String) ARP timeout is time how long ARP record is kept in ARP table after no packets are received from IP. Value auto equals to the value of arp-timeout in IP/Settings, default is 30s. Can use postfix `ms`, `s`, `m`, `h`, `d` for milliseconds, seconds, minutes, hours or days. If no postfix is set then seconds (s) is used. - `auto_negotiation` (Boolean) When enabled, the interface "advertises" its maximum capabilities to achieve the best connection possible. Note1: Auto-negotiation should not be disabled on one end only, otherwise Ethernet Interfaces may not work properly. Note2: Gigabit Ethernet and NBASE-T Ethernet links cannot work with auto-negotiation disabled. - `bandwidth` (String) Sets max rx/tx bandwidth in kbps that will be handled by an interface. TX limit is supported on all Atheros switch-chip ports. RX limit is supported only on Atheros8327/QCA8337 switch-chip ports. - `cable_settings` (String) Changes the cable length setting (only applicable to NS DP83815/6 cards) - `combo_mode` (String) When auto mode is selected, the port that was first connected will establish the link. In case this link fails, the other port will try to establish a new link. If both ports are connected at the same time (e.g. after reboot), the priority will be the SFP/SFP+ port. When sfp mode is selected, the interface will only work through SFP/SFP+ cage. When copper mode is selected, the interface will only work through RJ45 Ethernet port. - `comment` (String) - `disable_running_check` (Boolean) Disable running check. If this value is set to 'no', the router automatically detects whether the NIC is connected with a device in the network or not. Default value is 'yes' because older NICs do not support it. (only applicable to x86) - `disabled` (Boolean) - `fec_mode` (String) Changes Forward Error Correction (FEC) mode for SFP28, QSFP+ and QSFP28 interfaces. Same mode should be used on both link ends, otherwise FEC mismatch could result in non-working link or even false link-ups. - `full_duplex` (Boolean) Defines whether the transmission of data appears in two directions simultaneously, only applies when auto-negotiation is disabled. - `l2mtu` (Number) Layer2 Maximum transmission unit. [See](https://wiki.mikrotik.com/wiki/Maximum_Transmission_Unit_on_RouterBoards). - `loop_protect` (String) - `loop_protect_disable_time` (String) - `loop_protect_send_interval` (String) - `mac_address` (String) Media Access Control number of an interface. - `mdix_enable` (Boolean) Whether the MDI/X auto cross over cable correction feature is enabled for the port (Hardware specific, e.g. ether1 on RB500 can be set to yes/no. Fixed to 'yes' on other hardware.) - `mtu` (Number) Layer3 Maximum transmission unit - `poe_lldp_enabled` (Boolean) An option that enables LLDP for managing devices. - `poe_out` (String) PoE settings: (https://wiki.mikrotik.com/wiki/Manual:PoE-Out) - `poe_priority` (Number) PoE settings: (https://wiki.mikrotik.com/wiki/Manual:PoE-Out) - `poe_voltage` (String) An option that allows us to manually control the voltage outputs on the PoE port. - `power_cycle_interval` (String) An options that disables PoE-Out power for 5s between the specified intervals. - `power_cycle_ping_address` (String) An address to monitor. - `power_cycle_ping_enabled` (Boolean) An option that enables ping watchdog of power cycles on the port if a host does not respond to ICMP or MAC-Telnet packets. - `power_cycle_ping_timeout` (String) If the host does not respond over the specified period, the PoE-Out port is switched off for 5s. - `rx_flow_control` (String) When set to on, the port will process received pause frames and suspend transmission if required. auto is the same as on except when auto-negotiation=yes flow control status is resolved by taking into account what other end advertises. - `sfp_ignore_rx_los` (Boolean) An option to ignore RX LOS (Loss of Signal) status of the SFP module. - `sfp_rate_select` (String) Allows to control rate select pin for SFP ports. Values: high | low - `sfp_shutdown_temperature` (Number) The temperature in Celsius at which the interface will be temporarily turned off due to too high detected SFP module temperature (introduced v6.48).The default value for SFP/SFP+/SFP28 interfaces is 95, and for QSFP+/QSFP28 interfaces 80 (introduced v7.6). - `speed` (String) Sets interface data transmission speed which takes effect only when ```auto_negotiation``` is disabled. - `tx_flow_control` (String) When set to on, the port will generate pause frames to the upstream device to temporarily stop the packet transmission. Pause frames are only generated when some routers output interface is congested and packets cannot be transmitted anymore. Auto is the same as on except when auto-negotiation=yes flow control status is resolved by taking into account what other end advertises. ### Read-Only - `default_name` (String) The default name for an interface. - `id` (String) The ID of this resource. - `loop_protect_status` (String) - `orig_mac_address` (String) Original Media Access Control number of an interface. (read only) - `running` (Boolean) Whether interface is running. Note that some interface does not have running check and they are always reported as "running" - `slave` (Boolean) Whether interface is configured as a slave of another interface (for example Bonding) - `switch` (String) ID to which switch chip interface belongs to. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/ethernet get [print show-ids]] terraform import routeros_interface_ethernet.test "*0" #Or you can import a resource using one of its attributes terraform import routeros_interface_ethernet.test "name=xxx" ``` ================================================ FILE: docs/resources/interface_ethernet_switch.md ================================================ # routeros_interface_ethernet_switch (Resource) ## Example Usage ```terraform resource "routeros_interface_ethernet_switch" "sw0" { switch_id = 0 # Optional name = "new switch" } ``` ## Schema ### Required - `name` (String) Name of the switch. ### Optional - `cpu_flow_control` (Boolean) All switch chips have a special port that is called switchX-cpu, this is the CPU port for a switch chip, it is meant to forward traffic from a switch chip to the CPU, such a port is required for management traffic and for routing features. By default the switch chip ensures that this special CPU port is not congested and sends out Pause Frames when link capacity is exceeded to make sure the port is not oversaturated, this feature is called CPU Flow Control. Without this feature packets that might be crucial for routing or management purposes might get dropped. - `l3_hw_offloading` (Boolean) Layer 3 Hardware Offloading (L3HW, otherwise known as IP switching or HW routing) allows to offload some router features onto the switch chip. This allows reaching wire speeds when routing packets, which simply would not be possible with the CPU. - `mirror_egress_target` (String) Selects a single mirroring egress target port, only available on 88E6393X, 88E6191X and 88E6190 switch chips. Mirrored packets from `mirror-egress` (see the property in port menu) will be sent to the selected port. - `mirror_source` (String) Selects a single mirroring source port. Ingress and egress traffic will be sent to the mirror-target port. Note that mirror-target port has to belong to the same switch (see which port belongs to which switch in /interface ethernet menu). - `mirror_target` (String) Selects a single mirroring target port. Mirrored packets from mirror-source and mirror (see the property in rule and host table) will be sent to the selected port. - `qos_hw_offloading` (Boolean) Allows enabling QoS for the given switch chip (if the latter supports QoS). - `rspan` (Boolean) Enables Remote Switch Port Analyzer (RSPAN) feature on mirror-target. Traffic marked for ingress or egress mirroring is carried over a specified remote analyzer VLAN - `rspan-egress-vlan-id` and `rspan-ingress-vlan-id`. - `rspan_egress_vlan_id` (Number) RSPAN egress VLAN Id. - `rspan_ingress_vlan_id` (Number) RSPAN ingress VLAN Id. - `switch_id` (String) Switch-chip id. Default .id = *0 ### Read-Only - `id` (String) The ID of this resource. - `invalid` (Boolean) - `type` (String) Switch-chip type. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/ethernet/switch get [print show-ids]] terraform import routeros_interface_ethernet_switch.sw0 *0 ``` ================================================ FILE: docs/resources/interface_ethernet_switch_crs.md ================================================ # routeros_interface_ethernet_switch_crs (Resource) Resource for managing CRS (Cloud Router Switch) series device properties. ## Example Usage ```terraform resource "routeros_interface_ethernet_switch_crs" "sw0" { switch_id = 0 name = "new switch" drop_if_invalid_or_src_port_not_member_of_vlan_on_ports = ["ether1", "ether2", "ether3"] } ``` ## Schema ### Required - `name` (String) Name of the switch. ### Optional - `bridge_type` (String) The bridge type defines which VLAN tag is used as Lookup-VID. Lookup-VID serves as the VLAN key for all VLAN-based lookups. - `bypass_ingress_port_policing_for` (Set of String) Protocols that are excluded from Ingress Port Policing. (arp, dhcpv4, dhcpv6, eapol, igmp, mld, nd, pppoe-discovery, ripv1). - `bypass_l2_security_check_filter_for` (Set of String) Protocols that are excluded from Policy rule security check. (arp, dhcpv4, dhcpv6, eapol, igmp, mld, nd, pppoe-discovery, ripv1). - `bypass_vlan_ingress_filter_for` (Set of String) Protocols that are excluded from Ingress VLAN filtering. These protocols are not dropped if they have invalid VLAN. (arp, dhcpv4, dhcpv6,eapol, igmp, mld, nd, pppoe-discovery, ripv1). - `drop_if_invalid_or_src_port_not_member_of_vlan_on_ports` (Set of String) Ports that drop invalid and other port VLAN ID frames. - `drop_if_no_vlan_assignment_on_ports` (Set of String) Ports which drop frames if no MAC-based, Protocol-based VLAN assignment or Ingress VLAN Translation is applied. - `egress_mirror0` (Set of String) The first egress mirroring analyzer port or trunk and mirroring format:analyzer-configured - The packet is the same as the packet to the destination. VLAN format is modified based on the VLAN configurations of the analyzer port.modified - The packet is the same as the packet to the destination. VLAN format is modified based on the VLAN configurations of the egress port.original - Traffic is mirrored without any change to the original incoming packet format. But the service VLAN tag is stripped in the edge port. - `egress_mirror1` (Set of String) The second egress mirroring analyzer port or trunk and mirroring format:analyzer-configured - The packet is the same as the packet to the destination. VLAN format is modified based on the VLAN configurations of the analyzer port.modified - The packet is the same as the packet to the destination. VLAN format is modified based on the VLAN configurations of the egress port.original - Traffic is mirrored without any change to the original incoming packet format. But the service VLAN tag is stripped in the edge port. - `egress_mirror_ratio` (String) Proportion of egress mirrored packets compared to all packets. - `fdb_uses` (String) Analyzer port used for FDB-based mirroring. - `forward_unknown_vlan` (Boolean) Whether to allow forwarding VLANs that are not members of the VLAN table. - `ingress_mirror0` (Set of String) The first ingress mirroring analyzer port or trunk and mirroring format:analyzer-configured - The packet is the same as the packet to the destination. VLAN format is modified based on the VLAN configurations of the analyzer port.modified - The packet is the same as the packet to the destination. VLAN format is modified based on the VLAN configurations of the egress port.original - Traffic is mirrored without any change to the original incoming packet format. But the service VLAN tag is stripped in the edge port. - `ingress_mirror1` (Set of String) The second ingress mirroring analyzer port or trunk and mirroring format:analyzer-configured - The packet is the same as the packet to the destination. VLAN format is modified based on the VLAN configurations of the analyzer port.modified - The packet is the same as the packet to the destination. VLAN format is modified based on the VLAN configurations of the egress port.original - Traffic is mirrored without any change to the original incoming packet format. But the service VLAN tag is stripped in the edge port. - `ingress_mirror_ratio` (String) The proportion of ingress mirrored packets compared to all packets. - `mac_level_isolation` (Boolean) Globally enables or disables MAC level isolation. Once enabled, the switch will check the source and destination MAC address entries and their isolation-profile from the unicast forwarding table. By default, the switch will learn MAC addresses and place them into a promiscuous isolation profile. Other isolation profiles can be used when creating static unicast entries. If the source or destination MAC address is located on a promiscuous isolation profile, the packet is forwarded. If both source and destination MAC addresses are located on the same community1 or community2 isolation profile, the packet is forwarded. The packet is dropped when the source and destination MAC address isolation profile is isolated, or when the source and destination MAC address isolation profiles are from different communities (e.g. source MAC address is community1 and destination MAC address is community2). When MAC level isolation is globally disabled, the isolation is bypassed. - `mirror_egress_if_ingress_mirrored` (Boolean) When a packet is applied to both ingress and egress mirroring, only ingress mirroring is performed on the packet, if this setting is disabled. If this setting is enabled both mirroring types are applied. - `mirror_tx_on_mirror_port` (Boolean) - `mirrored_packet_drop_precedence` (String) Remarked drop precedence in mirrored packets. This QoS attribute is used for mirrored packet enqueuing or dropping. - `mirrored_packet_qos_priority` (Number) Remarked priority in mirrored packets. - `multicast_lookup_mode` (String) Lookup mode for IPv4 multicast bridging.dst-mac-and-vid-always - For all packet types lookup key is the destination MAC and VLAN ID.dst-ip-and-vid-for-ipv4 - For IPv4 packets lookup key is the destination IP and VLAN ID. For other packet types, the lookup key is the destination MAC and VLAN ID. - `override_existing_when_ufdb_full` (Boolean) Enable or disable to override existing entry which has the lowest aging value when UFDB is full. - `unicast_fdb_timeout` (String) Timeout for Unicast FDB entries. - `unknown_vlan_lookup_mode` (String) Lookup and learning mode for packets with invalid VLAN. - `use_cvid_in_one2one_vlan_lookup` (Boolean) Whether to use customer VLAN ID for 1:1 VLAN switching lookup. - `use_svid_in_one2one_vlan_lookup` (Boolean) Whether to use service VLAN ID for 1:1 VLAN switching lookup. - `vlan_uses` (String) Analyzer port used for VLAN-based mirroring. ### Read-Only - `id` (String) The ID of this resource. - `type` (String) Switch-chip type. ## Import Import is supported using the following syntax: ```shell terraform import routeros_interface_ethernet_switch_crs.sw0 . ``` ================================================ FILE: docs/resources/interface_ethernet_switch_crs_egress_vlan_tag.md ================================================ # routeros_interface_ethernet_switch_crs_egress_vlan_tag (Resource) Resource for managing CRS (Cloud Router Switch) series device properties. ## Example Usage ```terraform resource "routeros_interface_ethernet_switch_crs_egress_vlan_tag" "test" { vlan_id = 100 tagged_ports = ["ether1", "ether2"] } ``` ## Schema ### Optional - `comment` (String) - `disabled` (Boolean) - `tagged_ports` (Set of String) Ports that are tagged in egress. - `vlan_id` (Number) VLAN ID which is tagged in egress. ### Read-Only - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/ethernet/switch/egress-vlan-tag get [print show-ids]] terraform import routeros_interface_ethernet_switch_crs_egress_vlan_tag.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_ethernet_switch_crs_egress_vlan_tag.test "name=xxx" ``` ================================================ FILE: docs/resources/interface_ethernet_switch_crs_egress_vlan_translation.md ================================================ # routeros_interface_ethernet_switch_crs_egress_vlan_translation (Resource) Resource for managing CRS (Cloud Router Switch) series device properties. ## Example Usage ```terraform resource "routeros_interface_ethernet_switch_crs_egress_vlan_translation" "test" { ports = ["ether1"] service_vlan_format = "any" customer_vlan_format = "any" customer_vid = 100 new_customer_vid = 0 } ``` ## Schema ### Optional - `comment` (String) - `customer_dei` (String) Matching DEI of the customer tag. - `customer_pcp` (String) Matching PCP of the customer tag. - `customer_vid` (String) Matching the VLAN ID of the customer tag. - `customer_vlan_format` (String) Type of frames with customer tag for which VLAN translation rule is valid. - `disabled` (Boolean) - `new_customer_vid` (String) The new customer VLAN ID replaces the matching customer VLAN ID. If set to 4095 and ingress VLAN translation is used, then traffic is dropped. - `new_service_vid` (String) The new service VLAN ID replaces the matching service VLAN ID. - `pcp_propagation` (String) Enables or disables PCP propagation.If the port type is Edge, the customer PCP is copied from the service PCP.If the port type is Network, the service PCP is copied from the customer PCP. - `ports` (Set of String) Matching switch ports for VLAN translation rule. - `service_dei` (String) Matching DEI of the service tag. - `service_pcp` (String) Matching PCP of the service tag. - `service_vid` (String) Matching VLAN ID of the service tag. - `service_vlan_format` (String) Type of frames with service tag for which VLAN translation rule is valid. - `swap_vids` (String) ### Read-Only - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/ethernet/switch/egress-vlan-translation get [print show-ids]] terraform import routeros_interface_ethernet_switch_crs_egress_vlan_translation.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_ethernet_switch_crs_egress_vlan_translation.test "name=xxx" ``` ================================================ FILE: docs/resources/interface_ethernet_switch_crs_ingress_vlan_translation.md ================================================ # routeros_interface_ethernet_switch_crs_ingress_vlan_translation (Resource) Resource for managing CRS (Cloud Router Switch) series device properties. ## Example Usage ```terraform resource "routeros_interface_ethernet_switch_crs_ingress_vlan_translation" "test" { ports = ["ether1"] service_vlan_format = "any" customer_vlan_format = "any" customer_vid = 0 new_customer_vid = 100 sa_learning = true } ``` ## Schema ### Optional - `comment` (String) - `customer_dei` (String) Matching DEI of the customer tag. - `customer_pcp` (String) Matching PCP of the customer tag. - `customer_vid` (String) Matching the VLAN ID of the customer tag. - `customer_vlan_format` (String) Type of frames with customer tag for which VLAN translation rule is valid. - `disabled` (Boolean) - `new_customer_vid` (String) The new customer VLAN ID replaces the matching customer VLAN ID. If set to 4095 and ingress VLAN translation is used, then traffic is dropped. - `new_service_vid` (String) The new service VLAN ID replaces the matching service VLAN ID. - `pcp_propagation` (String) Enables or disables PCP propagation.If the port type is Edge, the customer PCP is copied from the service PCP.If the port type is Network, the service PCP is copied from the customer PCP. - `ports` (Set of String) Matching switch ports for VLAN translation rule. - `protocol` (String) Matching Ethernet protocol (only for Ingress VLAN Translation). - `sa_learning` (String) Enables or disables source MAC learning after VLAN translation (only for Ingress VLAN Translation). - `service_dei` (String) Matching DEI of the service tag. - `service_pcp` (String) Matching PCP of the service tag. - `service_vid` (String) Matching VLAN ID of the service tag. - `service_vlan_format` (String) Type of frames with service tag for which VLAN translation rule is valid. - `swap_vids` (String) ### Read-Only - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/ethernet/switch/ingress-vlan-translation get [print show-ids]] terraform import routeros_interface_ethernet_switch_crs_ingress_vlan_translation.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_ethernet_switch_crs_ingress_vlan_translation.test "name=xxx" ``` ================================================ FILE: docs/resources/interface_ethernet_switch_crs_vlan.md ================================================ # routeros_interface_ethernet_switch_crs_vlan (Resource) Resource for managing CRS (Cloud Router Switch) series device properties. ## Example Usage ```terraform resource "routeros_interface_ethernet_switch_crs_vlan" "test" { ports = ["ether1"] vlan_id = 10 } ``` ## Schema ### Required - `vlan_id` (Number) VLAN ID of the VLAN member entry. ### Optional - `comment` (String) - `disabled` (Boolean) - `flood` (Boolean) Enables or disables forced VLAN flooding per VLAN. If the feature is enabled, the result of the destination MAC lookup in the UFDB or MFDB is ignored,and the packet is forced to flood in the VLAN. - `ingress_mirror` (Boolean) Enable the ingress mirror per VLAN to support the VLAN-based mirror function. - `learn` (Boolean) Enables or disables source MAC learning for VLAN. - `ports` (String) Member ports of the VLAN. - `qos_group` (String) Defined QoS group from QoS group menu. - `svl` (Boolean) FDB lookup mode for lookup in UFDB and MFDB. - Shared VLAN Learning (svl) - learning/lookup is based on MAC addresses - not on VLAN IDs. - Independent VLAN Learning (ivl) - learning/lookup is based on both MAC addresses and VLAN IDs. ### Read-Only - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/ethernet/switch/vlan get [print show-ids]] terraform import routeros_interface_ethernet_switch_crs_vlan.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_ethernet_switch_crs_vlan.test "comment=xxx" ``` ================================================ FILE: docs/resources/interface_ethernet_switch_host.md ================================================ # routeros_interface_ethernet_switch_host (Resource) ## Example Usage ```terraform resource "routeros_interface_ethernet_switch_host" "test" { switch = "switch1" mac_address = "00:00:00:00:00:00" ports = ["ether1"] mirror = true } ``` ## Schema ### Required - `mac_address` (String) Host's MAC address. - `ports` (List of String) Name of the interface, static MAC address can be mapped to more that one port, including switch CPU port. - `switch` (String) Name of the switch to which the MAC address is going to be assigned to. ### Optional - `copy_to_cpu` (Boolean) Whether to send a frame copy to switch CPU port from a frame with matching MAC destination address (matching destination or source address for CRS3xx series switches). - `drop` (Boolean) Whether to drop a frame with matching MAC source address received on a certain port (matching destination or source address for CRS3xx series switches). - `mirror` (Boolean) Whether to send a frame copy to mirror-target port from a frame with matching MAC destination address (matching destination or source address for CRS3xx series switches). - `redirect_to_cpu` (Boolean) Whether to redirect a frame to switch CPU port from a frame with matching MAC destination address (matching destination or source address for CRS3xx series switches). - `share_vlan_learned` (Boolean) Whether the static host MAC address lookup is used with shared-VLAN-learning (SVL) or independent-VLAN-learning (IVL). The SVL mode is used for those VLAN entries that do not support IVL or IVL is disabled (independent-learning=no). - `vlan_id` (Number) VLAN ID for the statically added MAC address entry. ### Read-Only - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. - `invalid` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/ethernet/switch/host get [print show-ids]] terraform import routeros_interface_ethernet_switch_host.test *0 #Or you can import a resource using one of its attributes terraform import routeros_interface_ethernet_switch_host.test "name=xxx" ``` ================================================ FILE: docs/resources/interface_ethernet_switch_port.md ================================================ # routeros_interface_ethernet_switch_port (Resource) ## Example Usage ```terraform resource "routeros_interface_ethernet_switch_port" "test" { name = "ether1" vlan_mode = "check" } ``` ## Schema ### Required - `name` (String) Port name. ### Optional - `default_vlan_id` (String) Adds a VLAN tag with the specified VLAN ID on all untagged ingress traffic on a port, should be used with ```vlan-header``` set to ```always-strip``` on a port to configure the port to be the access port. For hybrid ports ```default-vlan-id``` is used to tag untagged traffic. If two ports have the same ```default-vlan-id```, then VLAN tag is not added since the switch chip assumes that traffic is being forwarded between access ports. - `mirror_egress` (Boolean) Whether to send egress packet copy to the `mirror-egress-target` port, only available on 88E6393X, 88E6191X and 88E6190 switch chips. - `mirror_ingress` (Boolean) Whether to send ingress packet copy to the `mirror-ingress-target` port, only available on 88E6393X, 88E6191X and 88E6190 switch chips. - `mirror_ingress_target` (String) Selects a single mirroring ingress target port, only available on 88E6393X, 88E6191X and 88E6190 switch chips. Mirrored packets from `mirror-ingress` will be sent to the selected port. - `vlan_header` (String) Sets action which is performed on the port for egress traffic. - `vlan_mode` (String) Changes the VLAN lookup mechanism against the VLAN Table for ingress traffic. ### Read-Only - `id` (String) The ID of this resource. - `invalid` (Boolean) - `running` (Boolean) - `switch` (String) Name of the switch. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/ethernet/switch/port get [print show-ids]] terraform import routeros_interface_ethernet_switch_port.test *1 #Or you can import a resource using one of its attributes terraform import routeros_interface_ethernet_switch_port.test "name=xxx" ``` ================================================ FILE: docs/resources/interface_ethernet_switch_port_isolation.md ================================================ # routeros_interface_ethernet_switch_port_isolation (Resource) ## Example Usage ```terraform resource "routeros_interface_ethernet_switch_port_isolation" "test" { name = "ether1" forwarding_override = "ether1" } ``` ## Schema ### Required - `name` (String) Port name. ### Optional - `forwarding_override` (String) Forces ingress traffic to be forwarded to a specific interface. Multiple interfaces can be specified by separating them with a comma. ### Read-Only - `id` (String) The ID of this resource. - `invalid` (Boolean) - `switch` (String) Name of the switch. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/ethernet/switch/port-isolation get [print show-ids]] terraform import routeros_interface_ethernet_switch_port_isolation.test *1 #Or you can import a resource using one of its attributes terraform import routeros_interface_ethernet_switch_port_isolation.test "name=xxx" ``` ================================================ FILE: docs/resources/interface_ethernet_switch_rule.md ================================================ # routeros_interface_ethernet_switch_rule (Resource) ## Example Usage ```terraform resource "routeros_interface_ethernet_switch_rule" "test" { switch = "switch1" ports = ["ether1"] copy_to_cpu = true } ``` ## Schema ### Required - `ports` (List of String) Name of the interface on which the rule will apply on the received traffic, multiple ports are allowed. - `switch` (String) Matching switch group on which will the rule apply. ### Optional - `comment` (String) - `copy_to_cpu` (Boolean) Whether to send a frame copy to switch CPU port from a frame with matching MAC destination address (matching destination or source address for CRS3xx series switches). - `disabled` (Boolean) - `dscp` (Number) Matching DSCP field of the packet. - `dst_address` (String) Matching destination IP address and mask. - `dst_address6` (String) Matching destination IPv6 address and mask. - `dst_mac_address` (String) Matching destination MAC address and mask. - `dst_port` (Number) Matching destination protocol port number or range. - `flow_label` (Number) Matching IPv6 flow label. - `mac_protocol` (String) Matching particular MAC protocol specified by protocol name or number (skips VLAN tags if any). - `mirror` (Boolean) Whether to send a frame copy to mirror-target port from a frame with matching MAC destination address (matching destination or source address for CRS3xx series switches). - `mirror_ports` (Set of String) Selects multiple mirroring target ports, only available on 88E6393X switch chip. Matched packets in the ACL rule will be copied and sent to selected ports. - `new_dst_ports` (Set of String) Changes the destination port as specified, multiple ports allowed, including a switch CPU port. An empty setting will drop the packet. When the parameter is not used, the packet will be accepted. - `new_vlan_id` (Number) Changes the VLAN ID to the specified value or adds a new VLAN tag if one was not already present (the property only applies to the Atheros8316, and 88E6393X switch chips). - `new_vlan_priority` (Number) Changes the VLAN priority field (priority code point, the property only applies to Atheros8327, QCA8337 and Atheros8316 switch chips). - `protocol` (String) Matching particular IP protocol specified by protocol name or number. - `rate` (Number) Sets ingress traffic limitation (bits per second) for matched traffic, can only be applied to the first 32 rule slots (the property only applies to Atheros8327/QCA8337 switch chips). - `redirect_to_cpu` (Boolean) Changes the destination port of a matching packet to the switch CPU. - `src_address` (String) Matching source IP address and mask. - `src_address6` (String) Matching source IPv6 address and mask. - `src_mac_address` (String) Matching source MAC address and mask. - `src_port` (Number) Matching source protocol port number or range. - `traffic_class` (Number) Matching IPv6 traffic class. - `vlan_header` (String) Matching VLAN header, whether the VLAN header is present or not (the property only applies to the Atheros8316, Atheros8327, QCA8337, 88E6393X switch chips). - `vlan_id` (Number) Matching VLAN ID (the property only applies to the Atheros8316, Atheros8327, QCA8337, 88E6393X switch chips). - `vlan_priority` (Number) Matching VLAN priority (priority code point). ### Read-Only - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. - `invalid` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/ethernet/switch/rule get [print show-ids]] terraform import routeros_interface_ethernet_switch_rule.test *0 #Or you can import a resource using one of its attributes terraform import routeros_interface_ethernet_switch_rule.test "name=xxx" ``` ================================================ FILE: docs/resources/interface_ethernet_switch_vlan.md ================================================ # routeros_interface_ethernet_switch_vlan (Resource) ## Example Usage ```terraform resource "routeros_interface_ethernet_switch_vlan" "test" { switch = "switch1" ports = ["ether1"] vlan_id = 10 independent_learning = true } ``` ## Schema ### Required - `ports` (List of String) Interface member list for the respective VLAN. - `switch` (String) Name of the switch for which the respective VLAN entry is intended for. ### Optional - `comment` (String) - `disabled` (Boolean) - `independent_learning` (Boolean) Whether to use shared-VLAN-learning (SVL) or independent-VLAN-learning (IVL). - `vlan_id` (Number) The VLAN ID for certain switch port configurations. ### Read-Only - `id` (String) The ID of this resource. - `invalid` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/ethernet/switch/vlan get [print show-ids]] terraform import routeros_interface_ethernet_switch_vlan.test *0 #Or you can import a resource using one of its attributes terraform import routeros_interface_ethernet_switch_vlan.test "name=xxx" ``` ================================================ FILE: docs/resources/interface_gre.md ================================================ # routeros_interface_gre (Resource) ## Example Usage ```terraform resource "routeros_interface_gre" "gre_hq" { name = "gre-hq-1" remote_address = "10.77.3.26" disabled = true } ``` ## Schema ### Required - `name` (String) Changing the name of this resource will force it to be recreated. > The links of other configuration properties to this resource may be lost! > Changing the name of the resource outside of a Terraform will result in a loss of control integrity for that resource! ### Optional - `allow_fast_path` (Boolean) Whether to allow FastPath processing. Must be disabled if IPsec tunneling is used. - `clamp_tcp_mss` (Boolean) Controls whether to change MSS size for received TCP SYN packets. When enabled, a router will change the MSS size for received TCP SYN packets if the current MSS size exceeds the tunnel interface MTU (taking into account the TCP/IP overhead). The received encapsulated packet will still contain the original MSS, and only after decapsulation the MSS is changed. - `comment` (String) - `disabled` (Boolean) - `dont_fragment` (String) - `dscp` (String) Set dscp value in GRE header to a fixed value '0..63' or 'inherit' from dscp value taken from tunnelled traffic. - `ipsec_secret` (String, Sensitive) When secret is specified, router adds dynamic IPsec peer to remote-address with pre-shared key and policy (by default phase2 uses sha1/aes128cbc). - `keepalive` (String) Tunnel keepalive parameter sets the time interval in which the tunnel running flag will remain even if the remote end of tunnel goes down. If configured time,retries fail, interface running flag is removed. Parameters are written in following format: `KeepaliveInterval,KeepaliveRetries` where `KeepaliveInterval` is time interval and `KeepaliveRetries` - number of retry attempts. `KeepaliveInterval` is integer 0..4294967295 - `local_address` (String) Source address of the tunnel packets, local on the router. - `mtu` (String) Layer3 Maximum transmission unit ('auto', 0 .. 65535). Look for the exact minimum value in the MikroTik documentation - `remote_address` (String) IP address of the remote end of the tunnel. ### Read-Only - `actual_mtu` (Number) - `id` (String) The ID of this resource. - `l2mtu` (Number) Layer2 Maximum transmission unit. [See](https://wiki.mikrotik.com/wiki/Maximum_Transmission_Unit_on_RouterBoards). - `running` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/gre get [print show-ids]] terraform import routeros_interface_gre.gre_hq "*1" #Or you can import a resource using one of its attributes terraform import routeros_interface_gre.gre_hq "name=xxx" ``` ================================================ FILE: docs/resources/interface_gre6.md ================================================ # routeros_interface_gre6 (Resource) ## Example Usage ```terraform resource "routeros_interface_gre6" "gre_hq" { name = "gre-hq-ipv6" remote_address = "2a02::2" disabled = true } ``` ## Schema ### Required - `name` (String) Changing the name of this resource will force it to be recreated. > The links of other configuration properties to this resource may be lost! > Changing the name of the resource outside of a Terraform will result in a loss of control integrity for that resource! ### Optional - `clamp_tcp_mss` (Boolean) Controls whether to change MSS size for received TCP SYN packets. When enabled, a router will change the MSS size for received TCP SYN packets if the current MSS size exceeds the tunnel interface MTU (taking into account the TCP/IP overhead). The received encapsulated packet will still contain the original MSS, and only after decapsulation the MSS is changed. - `comment` (String) - `disabled` (Boolean) - `dscp` (String) Set dscp value in GRE header to a fixed value '0..63' or 'inherit' from dscp value taken from tunnelled traffic. - `ipsec_secret` (String, Sensitive) When secret is specified, router adds dynamic IPsec peer to remote-address with pre-shared key and policy (by default phase2 uses sha1/aes128cbc). - `keepalive` (String) Tunnel keepalive parameter sets the time interval in which the tunnel running flag will remain even if the remote end of tunnel goes down. If configured time,retries fail, interface running flag is removed. Parameters are written in following format: `KeepaliveInterval,KeepaliveRetries` where `KeepaliveInterval` is time interval and `KeepaliveRetries` - number of retry attempts. `KeepaliveInterval` is integer 0..4294967295 - `local_address` (String) Source address of the tunnel packets, local on the router. - `mtu` (String) Layer3 Maximum transmission unit ('auto', 0 .. 65535). Look for the exact minimum value in the MikroTik documentation - `remote_address` (String) IP address of the remote end of the tunnel. ### Read-Only - `actual_mtu` (Number) - `id` (String) The ID of this resource. - `l2mtu` (Number) Layer2 Maximum transmission unit. [See](https://wiki.mikrotik.com/wiki/Maximum_Transmission_Unit_on_RouterBoards). - `running` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/gre6 get [print show-ids]] terraform import routeros_interface_gre6.gre_hq "*1" #Or you can import a resource using one of its attributes terraform import routeros_interface_gre6.gre_hq "name=gre-hq-ipv6" ``` ================================================ FILE: docs/resources/interface_ipip.md ================================================ # routeros_interface_ipip (Resource) ## Example Usage ```terraform resource "routeros_interface_ipip" "ipip_hq" { name = "ipip-hq-1" remote_address = "10.77.3.26" disabled = true } ``` ## Schema ### Required - `name` (String) Name of the ipip interface. ### Optional - `allow_fast_path` (Boolean) Whether to allow FastPath processing. Must be disabled if IPsec tunneling is used. - `clamp_tcp_mss` (Boolean) Controls whether to change MSS size for received TCP SYN packets. When enabled, a router will change the MSS size for received TCP SYN packets if the current MSS size exceeds the tunnel interface MTU (taking into account the TCP/IP overhead). The received encapsulated packet will still contain the original MSS, and only after decapsulation the MSS is changed. - `comment` (String) - `disabled` (Boolean) - `dont_fragment` (String) - `dscp` (String) Set dscp value in GRE header to a fixed value '0..63' or 'inherit' from dscp value taken from tunnelled traffic. - `ipsec_secret` (String, Sensitive) When secret is specified, router adds dynamic IPsec peer to remote-address with pre-shared key and policy (by default phase2 uses sha1/aes128cbc). - `keepalive` (String) Tunnel keepalive parameter sets the time interval in which the tunnel running flag will remain even if the remote end of tunnel goes down. If configured time,retries fail, interface running flag is removed. Parameters are written in following format: `KeepaliveInterval,KeepaliveRetries` where `KeepaliveInterval` is time interval and `KeepaliveRetries` - number of retry attempts. `KeepaliveInterval` is integer 0..4294967295 - `local_address` (String) Source address of the tunnel packets, local on the router. - `mtu` (String) Layer3 Maximum transmission unit ('auto', 0 .. 65535). Look for the exact minimum value in the MikroTik documentation - `remote_address` (String) IP address of the remote end of the tunnel. ### Read-Only - `actual_mtu` (Number) - `id` (String) The ID of this resource. - `l2mtu` (Number) Layer2 Maximum transmission unit. [See](https://wiki.mikrotik.com/wiki/Maximum_Transmission_Unit_on_RouterBoards). - `running` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/ipip get [print show-ids]] terraform import routeros_interface_ipip.ipip_hq "*1" #Or you can import a resource using one of its attributes terraform import routeros_interface_ipip.ipip_hq "name=xxx" ``` ================================================ FILE: docs/resources/interface_l2tp_client.md ================================================ # routeros_interface_l2tp_client (Resource) ## Example Usage ```terraform resource "routeros_interface_l2tp_client" "test" { name = "l2tp-test-out" connect_to = "127.0.0.1" user = "aaa" password = "bbb" } ``` ## Schema ### Required - `name` (String) Descriptive name of the interface. ### Optional - `add_default_route` (Boolean) Whether to add L2TP remote address as a default route. - `allow` (Set of String) Allowed authentication methods. - `allow_fast_path` (Boolean) Whether to allow FastPath processing. Must be disabled if IPsec tunneling is used. - `comment` (String) - `connect_to` (String) Remote address of L2TP server (if the address is in VRF table, VRF should be specified) `/interface l2tp-client` `add connect-to=192.168.88.1@vrf1 name=l2tp-out1 user=l2tp-client`. - `default_route_distance` (Number) Since v6.2, sets distance value applied to auto created default route, if add-default-route is also selected. - `dial_on_demand` (Boolean) Connects only when outbound traffic is generated. If selected, then route with gateway address from `10.112.112.0/24` network will be added while connection is not established. - `disabled` (Boolean) - `ipsec_secret` (String, Sensitive) Preshared key used when use-ipsec is enabled. - `keepalive_timeout` (Number) Since v6.0rc13, tunnel keepalive timeout in seconds. - `l2tp_proto_version` (String) Specify protocol version. - `l2tpv3_circuit_id` (String) Set the virtual circuit identifier to bind the one end of the L2TPv3 control channel. - `l2tpv3_cookie_length` (Number) Configures an L2TPv3 pseudowire static session cookie. - `l2tpv3_digest_hash` (String) Specifies which hash function to be used. - `max_mru` (Number) Maximum Receive Unit. Max packet size that L2TP interface will be able to receive without packet fragmentation. - `max_mtu` (Number) Maximum Transmission Unit. Max packet size that L2TP interface will be able to send without packet fragmentation. - `mrru` (String) Maximum packet size that can be received on the link. If a packet is bigger than tunnel MTU, it will be split into multiple packets, allowing full size IP or Ethernet packets to be sent over the tunnel. - `password` (String, Sensitive) Password used for authentication. - `profile` (String) Specifies which PPP profile configuration will be used when establishing the tunnel. - `random_source_port` (Boolean) - `src_address` (String) Specify source address. - `use_ipsec` (Boolean) When this option is enabled, dynamic IPSec peer configuration and policy (transport mode) is added to encapsulate L2TP connection into IPSec tunnel. Multiple L2tp/ipsec clients behind the same NAT will not work in this mode. To achieve such scenario, disable use-ipsec and set static policies for clients with enabled `tunnel=yes`, `level=unique` settings. - `use_peer_dns` (String) To use peer dns. - `user` (String) User name used for authentication. ### Read-Only - `id` (String) The ID of this resource. - `running` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/l2tp/client get [print show-ids]] terraform import routeros_interface_l2tp_client.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_l2tp_client.test "name=xxx" ``` ================================================ FILE: docs/resources/interface_list.md ================================================ # routeros_interface_list (Resource) ## Example Usage ```terraform resource "routeros_interface_list" "list" { name = "my-list" } ``` ## Schema ### Required - `name` (String) Changing the name of this resource will force it to be recreated. > The links of other configuration properties to this resource may be lost! > Changing the name of the resource outside of a Terraform will result in a loss of control integrity for that resource! ### Optional - `comment` (String) - `exclude` (String) - `include` (String) ### Read-Only - `builtin` (Boolean) - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/list get [print show-ids]] terraform import routeros_interface_list.list "*2000010" #Or you can import a resource using one of its attributes terraform import routeros_interface_list.list "name=xxx" ``` ================================================ FILE: docs/resources/interface_list_member.md ================================================ # routeros_interface_list_member (Resource) ## Example Usage ```terraform resource "routeros_interface_list_member" "list_member" { interface = "ether1" list = "my-list" } ``` ## Schema ### Required - `interface` (String) - `list` (String) ### Optional - `comment` (String) - `disabled` (Boolean) ### Read-Only - `dynamic` (Boolean) - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/list/member get [print show-ids]] terraform import routeros_interface_list_member.list_member "*0" #Or you can import a resource using one of its attributes terraform import routeros_interface_list_member.list_member "name=xxx" ``` ================================================ FILE: docs/resources/interface_lte.md ================================================ # routeros_interface_lte (Resource) ## Example Usage ```terraform resource "routeros_interface_lte" "test" { allow_roaming = false apn_profiles = "default" band = [] default_name = "lte1" disabled = false mtu = "1500" name = "lte1" network_mode = ["3g", "lte"] sms_protocol = null } ``` ## Schema ### Required - `name` (String) Descriptive name of the interface. ### Optional - `allow_roaming` (Boolean) Enable data roaming for connecting to other countries data-providers. Not all LTE modems support this feature. Some modems, that do not fully support this feature, will connect to the network but will not establish an IP data connection with allow-roaming set to no. - `apn_profiles` (String) Which APN profile to use for this interface. - `band` (Set of Number) LTE Frequency band used in communication [LTE Bands and bandwidths](https://en.wikipedia.org/wiki/LTE_frequency_bands#Frequency_bands_and_channel_bandwidths). - `comment` (String) - `disabled` (Boolean) - `modem_init` (String) Modem init string (AT command that will be executed at modem startup). - `mtu` (String) Layer3 Maximum transmission unit ('auto', 0 .. 65535). Look for the exact minimum value in the MikroTik documentation - `network_mode` (Set of String) Select/force mode for LTE interface to operate with. - `nr_band` (Set of Number) 5G NR Frequency band used in communication [5G NR Bands and bandwidths](https://en.wikipedia.org/wiki/5G_NR_frequency_bands). - `operator` (Number) Used to lock the device to a specific operator full PLMN number is used for the lock consisting of MCC+MNC. [PLMN codes](https://en.wikipedia.org/wiki/Public_land_mobile_network). - `pin` (String) SIM Card's PIN code. - `sms_protocol` (String) SMS functionality. `mbim`: uses MBIM driver. `at`: uses AT-Commands. `auto`: selects the appropriate option depending on the modem. - `sms_read` (Boolean) ### Read-Only - `default_name` (String) The default name for an interface. - `id` (String) The ID of this resource. - `inactive` (Boolean) - `running` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ get [print show-ids]] terraform import routeros_interface_lte.test *3 ``` ================================================ FILE: docs/resources/interface_lte_apn.md ================================================ # routeros_interface_lte_apn (Resource) ## Example Usage ```terraform resource "routeros_interface_lte_apn" "test" { name = "apn1" apn = "internet" authentication = "pap" } ``` ## Schema ### Required - `name` (String) APN profile name ### Optional - `add_default_route` (Boolean) Whether to add a default route to forward all traffic over the LTE interface. - `apn` (String) Service Provider's Access Point Name. - `authentication` (String) Allowed protocol to use for authentication. - `comment` (String) - `default_route_distance` (Number) Sets distance value applied to auto-created default route, if add-default-route is also selected. LTE route by default is with distance 2 to prefer wired routes over LTE. - `ip_type` (String) Requested PDN type. - `ipv6_interface` (String) Interface on which to advertise IPv6 prefix. - `number` (Number) APN profile number. - `passthrough_interface` (String) Interface to passthrough IP configuration (activates passthrough). - `passthrough_mac` (String) If set to auto, then will learn MAC from the first packet. - `passthrough_subnet_selection` (String) `auto` selects the smallest possible subnet to be used for the passthrough interface. `p2p` sets the passthrough interface subnet as `/32` and picks gateway address from `10.177.0.0/16` range. The gateway address stays the same until the apn configuration is changed. - `password` (String) Password used if any of the authentication protocols are active. - `use_network_apn` (Boolean) Parameter is available starting from RouterOS v7 and used only for MBIM modems. If set to yes, uses network provided APN. - `use_peer_dns` (Boolean) If set to yes, uses DNS received from LTE interface. - `user` (String) Username used if any of the authentication protocols are active. ### Read-Only - `default` (Boolean) It's the default item. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ get [print show-ids]] terraform import routeros_interface_lte_apn.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_lte_apn.test "name=xxx" ``` ================================================ FILE: docs/resources/interface_macvlan.md ================================================ # routeros_interface_macvlan (Resource) ## Example Usage ```terraform resource "routeros_interface_macvlan" "test" { interface = "ether1" name = "macvlan1" disabled = false } ``` ## Schema ### Required - `interface` (String) Name of the interface. - `name` (String) Changing the name of this resource will force it to be recreated. > The links of other configuration properties to this resource may be lost! > Changing the name of the resource outside of a Terraform will result in a loss of control integrity for that resource! ### Optional - `arp` (String) Address Resolution Protocol mode: * disabled - the interface will not use ARP * enabled - the interface will use ARP * local-proxy-arp - the router performs proxy ARP on the interface and sends replies to the same interface * proxy-arp - the router performs proxy ARP on the interface and sends replies to other interfaces * reply-only - the interface will only reply to requests originated from matching IP address/MAC address combinations which are entered as static entries in the ARP table. No dynamic entries will be automatically stored in the ARP table. Therefore for communications to be successful, a valid static entry must already exist. - `arp_timeout` (String) ARP timeout is time how long ARP record is kept in ARP table after no packets are received from IP. Value auto equals to the value of arp-timeout in IP/Settings, default is 30s. Can use postfix `ms`, `s`, `m`, `h`, `d` for milliseconds, seconds, minutes, hours or days. If no postfix is set then seconds (s) is used. - `comment` (String) - `disabled` (Boolean) - `loop_protect` (String) - `loop_protect_disable_time` (String) - `loop_protect_send_interval` (String) - `loop_protect_status` (Boolean) Loop protect status - `mac_address` (String) Static MAC address of the interface. A randomly generated MAC address will be assigned when not specified. - `mode` (String) Sets MACVLAN interface mode: * private - does not allow communication between MACVLAN instances on the same parent interface. * bridge - allows communication between MACVLAN instances on the same parent interface. - `mtu` (String) Layer3 Maximum transmission unit ('auto', 0 .. 65535). Look for the exact minimum value in the MikroTik documentation ### Read-Only - `id` (String) The ID of this resource. - `l2mtu` (Number) Layer2 Maximum transmission unit. [See](https://wiki.mikrotik.com/wiki/Maximum_Transmission_Unit_on_RouterBoards). - `running` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/macvlan get [print show-ids]] terraform import routeros_interface_macvlan.test "*0" #Or you can import a resource using one of its attributes terraform import routeros_interface_macvlan.test "name=xxx" ``` ================================================ FILE: docs/resources/interface_ovpn_client.md ================================================ # routeros_interface_ovpn_client (Resource) ## Schema ### Required - `connect_to` (String) Remote address of the OVPN server. - `name` (String) Descriptive name of the interface. - `user` (String) User name used for authentication. ### Optional - `add_default_route` (Boolean) Whether to add OVPN remote address as a default route. - `auth` (String) Authentication methods that the server will accept. - `certificate` (String) Name of the client certificate. - `cipher` (String) Allowed ciphers. - `comment` (String) - `disabled` (Boolean) - `mac_address` (String) Mac address of OVPN interface. Will be automatically generated if not specified. - `max_mtu` (Number) Maximum Transmission Unit. Max packet size that the OVPN interface will be able to send without packet fragmentation. - `mode` (String) Layer3 or layer2 tunnel mode (alternatively tun, tap) - `password` (String, Sensitive) Password used for authentication. - `port` (Number) Port to connect to. - `profile` (String) Specifies which PPP profile configuration will be used when establishing the tunnel. - `protocol` (String) Indicates the protocol to use when connecting with the remote endpoint. - `route_nopull` (Boolean) Specifies whether to allow the OVPN server to add routes to the OVPN client instance routing table. - `tls_version` (String) Specifies which TLS versions to allow. - `use_peer_dns` (Boolean) Whether to add DNS servers provided by the OVPN server to IP/DNS configuration. - `verify_server_certificate` (Boolean) Checks the certificates CN or SAN against the "connect-to" parameter. The IP or hostname must be present in the server's certificate. ### Read-Only - `hw_crypto` (Boolean) - `id` (String) The ID of this resource. - `running` (Boolean) ================================================ FILE: docs/resources/interface_ovpn_server.md ================================================ # routeros_interface_ovpn_server (Resource) ## Example Usage ```terraform resource "routeros_interface_ovpn_server" "user1" { name = "ovpn-in1" user = "user1" depends_on = [routeros_ovpn_server.server] } ``` ## Schema ### Required - `name` (String) Interface name (Example: ovpn-in1). ### Optional - `comment` (String) - `disabled` (Boolean) - `user` (String) User name used for authentication. ### Read-Only - `client_address` (String) The address of the remote side. - `encoding` (String) Encryption characteristics. - `id` (String) The ID of this resource. - `mtu` (Number) Layer2 Maximum transmission unit. [See](https://wiki.mikrotik.com/wiki/Maximum_Transmission_Unit_on_RouterBoards). - `running` (Boolean) - `uptime` (String) Connection uptime. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/ovpn-server get [print show-ids]] terraform import routeros_interface_ovpn_server.user1 *29 #Or you can import a resource using one of its attributes terraform import routeros_interface_ovpn_server.user1 "name=xxx" ``` ================================================ FILE: docs/resources/interface_pppoe_client.md ================================================ # routeros_interface_pppoe_client (Resource) ## Example Usage ```terraform resource "routeros_interface_pppoe_client" "test" { interface = "ether1" password = "StrongPass" service_name = "pppoeservice" name = "PPPoE-Out" disabled = false user = "MT-User" } ``` ## Schema ### Required - `interface` (String) Interface name on which client will run. - `name` (String) Name of the PPPoE interface. ### Optional - `ac_name` (String) Access Concentrator name, this may be left blank and the client will connect to any access concentrator on the broadcast domain. - `add_default_route` (Boolean) Enable/Disable whether to add default route automatically. - `allow` (Set of String) Allowed authentication methods, by default all methods are allowed. - `comment` (String) - `default_route_distance` (Number) sets distance value applied to auto created default route, if add-default-route is also selected. - `dial_on_demand` (Boolean) connects to AC only when outbound traffic is generated. If selected, then route with gateway address from 10.112.112.0/24 network will be added while connection is not established. - `disabled` (Boolean) - `host_uniq` (String) - `keepalive_timeout` (Number) Sets keepalive timeout in seconds. - `max_mru` (String) Maximum Receive Unit. - `max_mtu` (String) Maximum Transmission Unit. - `mrru` (String) Maximum packet size (512..65535 or disabled) that can be received on the link. If a packet is bigger than tunnel MTU, it will be split into multiple packets, allowing full size IP or Ethernet packets to be sent over the tunnel. - `password` (String, Sensitive) Password used to authenticate. - `profile` (String) Specifies which PPP profile configuration will be used when establishing the tunnel. - `service_name` (String) Specifies the service name set on the access concentrator, can be left blank to connect to any PPPoE server. - `use_peer_dns` (Boolean) Enable/disable getting DNS settings from the peer. - `user` (String) Username used for authentication. ### Read-Only - `id` (String) The ID of this resource. - `invalid` (Boolean) - `running` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/pppoe-client get [print show-ids]] terraform import routeros_interface_pppoe_client.test "*0" #Or you can import a resource using one of its attributes terraform import routeros_interface_pppoe_client.test "name=xxx" ``` ================================================ FILE: docs/resources/interface_pppoe_server.md ================================================ # routeros_interface_pppoe_server (Resource) ## Example Usage ```terraform resource "routeros_interface_pppoe_server" "test" { comment = "comment" disabled = true name = "pppoe-in1" user = "" service = "" } ``` ## Schema ### Required - `name` (String) ### Optional - `authentication` (Set of String) Authentication algorithm. - `comment` (String) - `default_profile` (String) - `disabled` (Boolean) - `interface` (String) Interface that the clients are connected to - `keepalive_timeout` (String) Defines the time period (in seconds) after which the router is starting to send keepalive packets every second. If there is no traffic and no keepalive responses arrive for that period of time (i.e. 2 * keepalive-timeout), the non responding client is proclaimed disconnected. - `max_mru` (Number) Maximum Receive Unit. The optimal value is the MTU of the interface the tunnel is working over reduced by 20 (so, for 1500-byte Ethernet link, set the MTU to 1480 to avoid fragmentation of packets). - `max_mtu` (Number) Maximum Transmission Unit. The optimal value is the MTU of the interface the tunnel is working over reduced by 20 (so, for 1500-byte Ethernet link, set the MTU to 1480 to avoid fragmentation of packets). - `max_sessions` (Number) Maximum number of clients that the AC can serve. '0' = no limitations. - `mrru` (String) Maximum packet size that can be received on the link. If a packet is bigger than tunnel MTU, it will be split into multiple packets, allowing full size IP or Ethernet packets to be sent over the tunnel. - `one_session_per_host` (Boolean) Allow only one session per host (determined by MAC address). If a host tries to establish a new session, the old one will be closed. - `pppoe_over_vlan_range` (Number) This setting allows a PPPoE server to operate over 802.1Q VLANs. By default, a PPPoE server only accepts untagged packets on its interface. However, in scenarios where clients are on separate VLANs, instead of creating multiple 802.1Q VLAN interfaces and bridging them together or configuring individual PPPoE servers for each VLAN, you can specify the necessary VLANs directly in the PPPoE server settings. When you specify the VLAN IDs, the PPPoE server will accept both untagged packets and 802.1Q tagged packets from clients, and it will reply using the same VLAN. This setting can also be applied to both CVLAN and SVLAN interfaces. For example, when the use-service-tag=yes option is used on a VLAN interface, enabling QinQ setups as well. The setting supports a range of VLAN IDs, as well as individual VLANs specified using comma-separated values. For example: pppoe-over-vlan-range=100-115,120,122,128-130. - `service` (String) This attribute is required in the ROS 7 version. - `service_name` (String) The PPPoE service name. Server will accept clients which sends PADI message with service-names that matches this setting or if service-name field in PADI message is not set. - `user` (String) This attribute is required in the ROS 7 version. ### Read-Only - `id` (String) The ID of this resource. - `running` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/pppoe/server get [print show-ids]] terraform import routeros_interface_pppoe_server.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_pppoe_server.test "name=xxx" ``` ================================================ FILE: docs/resources/interface_sstp_client.md ================================================ # routeros_interface_sstp_client (Resource) ## Schema ### Required - `connect_to` (String) Remote address of the SSTP server. - `name` (String) Descriptive name of the interface. - `password` (String, Sensitive) Password used for authentication. - `user` (String) User name used for authentication. ### Optional - `add_default_route` (Boolean) Whether to add L2TP remote address as a default route. - `add_sni` (Boolean) Enables/disables service. - `authentication` (Set of String) Authentication algorithm. - `certificate` (String) Name of the certificate in use. - `ciphers` (String) Allowed ciphers. - `comment` (String) - `default_route_distance` (String) Sets distance value applied to auto created default route, if add-default-route is also selected. - `dial_on_demand` (Boolean) Connects only when outbound traffic is generated. If selected, then route with gateway address from 10.112.112.0/24 network will be added while connection is not established. - `disabled` (Boolean) - `http_proxy` (String) Proxy address field. - `keepalive_timeout` (String) Sets keepalive timeout in seconds. - `max_mru` (Number) Maximum Receive Unit. - `max_mtu` (Number) Maximum Transmission Unit. - `mrru` (String) Maximum packet size that can be received on the link. If a packet is bigger than tunnel MTU, it will be split into multiple packets, allowing full size IP or Ethernet packets to be sent over the tunnel. - `pfs` (Boolean) Specifies which TLS authentication to use. With pfs=yes, TLS will use ECDHE-RSA- and DHE-RSA-. For maximum security setting pfs=required will use only ECDHE. - `port` (String) Sets port used. - `profile` (String) Specifies which PPP profile configuration will be used when establishing the tunnel. - `proxy_port` (String) Sets proxy port. - `tls_version` (String) Specifies which TLS versions to allow. - `verify_server_address_from_certificate` (Boolean) SSTP client will verify server address in certificate. - `verify_server_certificate` (Boolean) SSTP client will verify server certificate. ### Read-Only - `hw_crypto` (Boolean) - `id` (String) The ID of this resource. - `running` (Boolean) ================================================ FILE: docs/resources/interface_sstp_server.md ================================================ # routeros_interface_sstp_server (Resource) ## Schema ### Optional - `authentication` (Set of String) Authentication algorithm. - `certificate` (String) Name of the certificate in use. - `ciphers` (String) Allowed ciphers. - `default_profile` (String) Default profile to use. - `enabled` (Boolean) Enables/disables service. - `keepalive_timeout` (String) Sets keepalive timeout in seconds. - `max_mru` (Number) Maximum Receive Unit. - `max_mtu` (Number) Maximum Transmission Unit. - `mrru` (String) Maximum packet size that can be received on the link. If a packet is bigger than tunnel MTU, it will be split into multiple packets, allowing full size IP or Ethernet packets to be sent over the tunnel. - `pfs` (Boolean) Specifies which TLS authentication to use. With pfs=yes, TLS will use ECDHE-RSA- and DHE-RSA-. For maximum security setting pfs=required will use only ECDHE. - `port` (String) Sets port used. - `tls_version` (String) Specifies which TLS versions to allow. - `verify_client_certificate` (Boolean) SSTP server will verify client certificate. ### Read-Only - `id` (String) The ID of this resource. ================================================ FILE: docs/resources/interface_veth.md ================================================ # routeros_interface_veth (Resource) ## Example Usage ```terraform resource "routeros_interface_veth" "test" { name = "veth-test" address = ["192.168.120.2/24"] gateway = "192.168.120.1" comment = "Virtual interface" } ``` ## Schema ### Required - `name` (String) Interface name. ### Optional - `address` (Set of String) Ip address. - `comment` (String) - `dhcp` (Boolean) - `disabled` (Boolean) - `gateway` (String) Gateway IP address. - `gateway6` (String) Gateway IPv6 address. - `mac_address` (String) MAC address. ### Read-Only - `id` (String) The ID of this resource. - `running` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/veth get [print show-ids]] terraform import routeros_interface_veth.test "*0" #Or you can import a resource using one of its attributes terraform import routeros_interface_veth.test "name=xxx" ``` ================================================ FILE: docs/resources/interface_vlan.md ================================================ # routeros_interface_vlan (Resource) ## Example Usage ```terraform resource "routeros_interface_vlan" "interface_vlan" { interface = "bridge" name = "VLAN_TEST" vlan_id = 50 } ``` ## Schema ### Required - `interface` (String) Name of the interface. - `name` (String) Changing the name of this resource will force it to be recreated. > The links of other configuration properties to this resource may be lost! > Changing the name of the resource outside of a Terraform will result in a loss of control integrity for that resource! ### Optional - `arp` (String) Address Resolution Protocol mode: * disabled - the interface will not use ARP * enabled - the interface will use ARP * local-proxy-arp - the router performs proxy ARP on the interface and sends replies to the same interface * proxy-arp - the router performs proxy ARP on the interface and sends replies to other interfaces * reply-only - the interface will only reply to requests originated from matching IP address/MAC address combinations which are entered as static entries in the ARP table. No dynamic entries will be automatically stored in the ARP table. Therefore for communications to be successful, a valid static entry must already exist. - `arp_timeout` (String) ARP timeout is time how long ARP record is kept in ARP table after no packets are received from IP. Value auto equals to the value of arp-timeout in IP/Settings, default is 30s. Can use postfix `ms`, `s`, `m`, `h`, `d` for milliseconds, seconds, minutes, hours or days. If no postfix is set then seconds (s) is used. - `comment` (String) - `disabled` (Boolean) - `hw_offloaded` (Boolean) - `loop_protect` (String) - `loop_protect_disable_time` (String) - `loop_protect_send_interval` (String) - `mtu` (String) Layer3 Maximum transmission unit ('auto', 0 .. 65535). Look for the exact minimum value in the MikroTik documentation - `mvrp` (Boolean) Specifies whether this VLAN should declare its attributes through Multiple VLAN Registration Protocol (MVRP) as an applicant (available since RouterOS 7.15). It can be used to register the VLAN with connected bridges that support MVRP. This property only has an effect when use-service-tag is disabled. - `use_service_tag` (Boolean) - `vlan_id` (Number) Virtual LAN identifier or tag that is used to distinguish VLANs. Must be equal for all computers that belong to the same VLAN. ### Read-Only - `id` (String) The ID of this resource. - `l2mtu` (Number) Layer2 Maximum transmission unit. [See](https://wiki.mikrotik.com/wiki/Maximum_Transmission_Unit_on_RouterBoards). - `loop_protect_status` (String) - `mac_address` (String) Current mac address. - `running` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/vlan get [print show-ids]] terraform import routeros_interface_vlan.interface_vlan "*1" #Or you can import a resource using one of its attributes terraform import routeros_interface_vlan.interface_vlan "name=xxx" ``` ================================================ FILE: docs/resources/interface_vrrp.md ================================================ # routeros_interface_vrrp (Resource) ## Example Usage ```terraform resource "routeros_interface_vrrp" "interface_vrrp" { interface = "bridge" name = "lan_vrrp" } ``` ## Schema ### Required - `interface` (String) Name of the interface. - `name` (String) Changing the name of this resource will force it to be recreated. > The links of other configuration properties to this resource may be lost! > Changing the name of the resource outside of a Terraform will result in a loss of control integrity for that resource! ### Optional - `arp` (String) Address Resolution Protocol mode: * disabled - the interface will not use ARP * enabled - the interface will use ARP * local-proxy-arp - the router performs proxy ARP on the interface and sends replies to the same interface * proxy-arp - the router performs proxy ARP on the interface and sends replies to other interfaces * reply-only - the interface will only reply to requests originated from matching IP address/MAC address combinations which are entered as static entries in the ARP table. No dynamic entries will be automatically stored in the ARP table. Therefore for communications to be successful, a valid static entry must already exist. - `arp_timeout` (String) ARP timeout is time how long ARP record is kept in ARP table after no packets are received from IP. Value auto equals to the value of arp-timeout in IP/Settings, default is 30s. Can use postfix `ms`, `s`, `m`, `h`, `d` for milliseconds, seconds, minutes, hours or days. If no postfix is set then seconds (s) is used. - `authentication` (String) Authentication method to use for VRRP advertisement packets. - `comment` (String) - `connection_tracking_mode` (String) Specifies the mode for connection tracking synchronization. This setting is only relevant when `sync-connection-tracking=yes` is enabled. - `connection_tracking_port` (Number) Specifies UDP port for connection tracking synchronization. This setting is only relevant when `sync-connection-tracking=yes` is enabled. - `disabled` (Boolean) - `group_authority` (String) Allows combining multiple VRRP interfaces to maintain the same VRRP status within the group. `group_authority` was previously called `group_master`, `group_master` is kept for compatibility with scripts, but if both are set only `group_authority` will be taken into account. - `group_master` (String) Allows combining multiple VRRP interfaces to maintain the same VRRP status within the group. `group_authority` was previously called `group_master`, `group_master` is kept for compatibility with scripts, but if both are set only `group_authority` will be taken into account. - `interval` (String) VRRP update interval in seconds. Defines how often master sends advertisement packets. - `on_backup` (String) Script to execute when the node is switched to the backup state. - `on_fail` (String) Script to execute when the node fails. - `on_master` (String) Script to execute when the node is switched to master state. - `password` (String, Sensitive) Password required for authentication. Can be ignored if authentication is not used. - `preemption_mode` (Boolean) Whether the master node always has the priority. When set to `no` the backup node will not be elected to be a master until the current master fails, even if the backup node has higher priority than the current master. This setting is ignored if the owner router becomes available - `priority` (Number) Priority of VRRP node used in Master election algorithm. A higher number means higher priority. `255` is reserved for the router that owns VR IP and `0` is reserved for the Master router to indicate that it is releasing responsibility. - `remote_address` (String) Specifies the remote address of the other VRRP router for syncing connection tracking. If not set, the system autodetects the remote address via VRRP. The remote address is used only if `sync_connection_tracking = true`.Sync connection tracking uses UDP port 8275. - `sync_connection_tracking` (Boolean) Synchronize connection tracking entries from Master to Backup device. - `v3_protocol` (String) A protocol that will be used by VRRPv3. Valid only if the version is 3. - `version` (Number) Which VRRP version to use. - `vrid` (Number) Virtual Router identifier. Each Virtual router must have a unique id number. ### Read-Only - `id` (String) The ID of this resource. - `invalid` (Boolean) - `mac_address` (String) Current mac address. - `mtu` (Number) Layer2 Maximum transmission unit. [See](https://wiki.mikrotik.com/wiki/Maximum_Transmission_Unit_on_RouterBoards). - `running` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/vrrp get [print show-ids]] terraform import routeros_interface_vrrp.interface_vrrp "*0" #Or you can import a resource using one of its attributes terraform import routeros_interface_vrrp.interface_vrrp "name=xxx" ``` ================================================ FILE: docs/resources/interface_vxlan.md ================================================ # routeros_interface_vxlan (Resource) ## Example Usage ```terraform resource "routeros_interface_vxlan" "test" { name = "vxlan1-test" vni = 10 } ``` ## Schema ### Required - `name` (String) Name of the interface. ### Optional - `allow_fast_path` (Boolean) Whether to allow Fast Path processing. Fragmented and flooded packets over VXLAN are redirected via a slow path. Fast Path is disabled for VXLAN interface that uses IPv6 VTEP version or VRF. The setting is available since RouterOS version 7.8. - `arp` (String) Address Resolution Protocol mode: * disabled - the interface will not use ARP * enabled - the interface will use ARP * local-proxy-arp - the router performs proxy ARP on the interface and sends replies to the same interface * proxy-arp - the router performs proxy ARP on the interface and sends replies to other interfaces * reply-only - the interface will only reply to requests originated from matching IP address/MAC address combinations which are entered as static entries in the ARP table. No dynamic entries will be automatically stored in the ARP table. Therefore for communications to be successful, a valid static entry must already exist. - `arp_timeout` (String) ARP timeout is time how long ARP record is kept in ARP table after no packets are received from IP. Value auto equals to the value of arp-timeout in IP/Settings, default is 30s. Can use postfix `ms`, `s`, `m`, `h`, `d` for milliseconds, seconds, minutes, hours or days. If no postfix is set then seconds (s) is used. - `checksum` (Boolean) Setting controls whether a UDP checksum is calculated in the transmitted outer VXLAN packets: - `no` - the UDP checksum is set to zero in transmitted outer packets. This also allows receiving VXLAN packets over IPv6 that have a zero UDP checksum. - `yes` - the UDP checksum is calculated in transmitted outer packets. If hardware offloading is used for packet transmission, this setting is ignored, and the behavior defaults to sending packets with a zero UDP checksum. - `comment` (String) - `disabled` (Boolean) - `dont_fragment` (String) The Don't Fragment (DF) flag controls whether a packet can be broken into smaller packets, called fragments, before being sent over a network. When configuring VXLAN, this setting determines the presence of the DF flag on the outer IPv4 header and can control packet fragmentation if the encapsulated packet exceeds the outgoing interface MTU. This setting has three options: * disabled - the DF flag is not set on the outer IPv4 header, which means that packets can be fragmented if they are too large to be sent over the outgoing interface. This also allows packet fragmentation when VXLAN uses IPv6 underlay. * enabled - the DF flag is always set on the outer IPv4 header, which means that packets will not be fragmented and will be dropped if they exceed the outgoing interface's MTU. This also avoids packet fragmentation when VXLAN uses IPv6 underlay. * inherit - The DF flag on the outer IPv4 header is based on the inner IPv4 DF flag. If the inner IPv4 header has the DF flag set, the outer IPv4 header will also have it set. If the packet exceeds the outgoing interface's MTU and DF is set, it will be dropped. If the inner packet is non-IP, the outer IPv4 header will not have the DF flag set and packets can be fragmented. If the inner packet is IPv6, the outer IPv4 header will always set the DF flag and packets cannot be fragmented. Note that when VXLAN uses IPv6 underlay, this setting does not have any effect and is treated the same as disabled. The setting is available since RouterOS version 7.8. - `group` (String) When specified, a multicast group address can be used to forward broadcast, unknown-unicast, and multicast traffic between VTEPs. This property requires specifying the interface setting. The interface will use IGMP or MLD to join the specified multicast group, make sure to add the necessary PIM and IGMP/MDL configuration. When this property is set, the vteps-ip-version automatically gets updated to the used multicast IP version. - `hw` (Boolean) - `interface` (String) Interface name used for multicast forwarding. This property requires specifying the group setting. - `learning` (Boolean) Setting controls whether inner source MAC addresses and remote VTEP IP/IPv6 addresses are learned dynamically from received packets. - `local_address` (String) Specifies the local source address for the VXLAN interface. If not set, one IP address of the egress interface will be selected as a source address for VXLAN packets. When the property is set, the vteps-ip-version automatically gets updated to the used local IP version. The setting is available since RouterOS version 7.7. - `loop_protect` (String) - `loop_protect_disable_time` (String) - `loop_protect_send_interval` (String) - `mac_address` (String) Static MAC address of the interface. A randomly generated MAC address will be assigned when not specified. - `max_fdb_size` (Number) Limits the maximum number of MAC addresses that VXLAN can store in the forwarding database (FDB). - `mtu` (String) Layer3 Maximum transmission unit ('auto', 0 .. 65535). Look for the exact minimum value in the MikroTik documentation - `port` (Number) Used UDP port number. - `rem_csum` (String) Changes the Remote Checksum Offload (RCO) settings for VXLAN interface. RCO is a technique for eliding the inner checksum of an encapsulated datagram, allowing the outer checksum to be offloaded by network driver. It does, however, involve a change to the encapsulation protocols, which the receiver must also support. For this reason, it is disabled by default and setting is available to ensure compatibility with systems that rely on this feature. If hardware offloading is used, this setting is ignored, and the behavior defaults to `none`. - `vni` (Number) VXLAN Network Identifier (VNI). - `vrf` (String) The VRF table this resource operates on. - `vteps_ip_version` (String) Used IP protocol version for statically configured VTEPs. The RouterOS VXLAN interface does not support dual-stack, any configured remote VTEPs with the opposite IP version will be ignored. When multicast group or local-address properties are set, the vteps-ip-version automatically gets updated to the used IP version. The setting is available since RouterOS version 7.6. ### Read-Only - `hw_offloaded` (Boolean) Indicates whether the route is eligible to be hardware offloaded on supported hardware. - `id` (String) The ID of this resource. - `l2mtu` (Number) Layer2 Maximum transmission unit. [See](https://wiki.mikrotik.com/wiki/Maximum_Transmission_Unit_on_RouterBoards). - `loop_protect_status` (String) - `running` (Boolean) - `ttl` (String) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/vxlan get [print show-ids]] terraform import routeros_interface_vxlan.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_vxlan.test "name=vxlan1" ``` ================================================ FILE: docs/resources/interface_vxlan_vteps.md ================================================ # routeros_interface_vxlan_vteps (Resource) ## Example Usage ```terraform resource "routeros_interface_vxlan" "test-2" { name = "vxlan2-test" vni = 11 } resource "routeros_interface_vxlan_vteps" "test" { interface = routeros_interface_vxlan.test-2.name remote_ip = "192.168.10.10" } ``` ## Schema ### Required - `interface` (String) Name of the interface. ### Optional - `comment` (String) - `disabled` (Boolean) - `port` (Number) Used UDP port number. - `remote_ip` (String) The IPv4 or IPv6 destination address of remote VTEP. ### Read-Only - `hw_offloaded` (Boolean) Indicates whether the route is eligible to be hardware offloaded on supported hardware. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/vxlan/vteps get [print show-ids]] terraform import routeros_interface_vxlan_vteps.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_vxlan_vteps.test "interface=vxlan2-test" ``` ================================================ FILE: docs/resources/interface_w60g.md ================================================ # routeros_interface_w60g (Resource) ## Example Usage ```terraform resource "routeros_interface_w60g" "test" { make = "wlan60-1" password = "put_your_safe_password_here" ssid = "put_your_new_ssid_here" disabled = false mode = "ap-bridge" } ``` ## Schema ### Required - `name` (String) Name of the interface. ### Optional - `arp` (String) Address Resolution Protocol mode: * disabled - the interface will not use ARP * enabled - the interface will use ARP * local-proxy-arp - the router performs proxy ARP on the interface and sends replies to the same interface * proxy-arp - the router performs proxy ARP on the interface and sends replies to other interfaces * reply-only - the interface will only reply to requests originated from matching IP address/MAC address combinations which are entered as static entries in the ARP table. No dynamic entries will be automatically stored in the ARP table. Therefore for communications to be successful, a valid static entry must already exist. - `arp_timeout` (String) ARP timeout is time how long ARP record is kept in ARP table after no packets are received from IP. Value auto equals to the value of arp-timeout in IP/Settings, default is 30s. Can use postfix `ms`, `s`, `m`, `h`, `d` for milliseconds, seconds, minutes, hours or days. If no postfix is set then seconds (s) is used. - `comment` (String) - `disabled` (Boolean) - `frequency` (String) Frequency used in communication (Only active on bridge device). - `isolate_stations` (Boolean) Don't allow communication between connected clients (from RouterOS 6.41). - `l2mtu` (Number) Layer2 Maximum transmission unit. [See](https://wiki.mikrotik.com/wiki/Maximum_Transmission_Unit_on_RouterBoards). - `mac_address` (String) MAC address of the radio interface. - `mdmg_fix` (Boolean) Experimental feature working only on wAP60Gx3 devices, providing better point to multi point stability in some cases. - `mode` (String) Operation mode. - `mtu` (String) Layer3 Maximum transmission unit ('auto', 0 .. 65535). Look for the exact minimum value in the MikroTik documentation - `password` (String, Sensitive) Password used for AES encryption. - `put_stations_in_bridge` (String) Put newly created station device interfaces in this bridge. - `region` (String) Parameter to limit frequency use. - `scan_list` (Set of String) Scan list to limit connectivity over frequencies in station mode. - `ssid` (String) SSID (service set identifier) is a name that identifies wireless network (0..32 char). - `tx_sector` (Number) Disables beamforming and locks to selected radiation pattern. ### Read-Only - `id` (String) The ID of this resource. - `running` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/w60g get [print show-ids]] terraform import routeros_interface_w60g.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_w60g.test "name=xxx" ``` ================================================ FILE: docs/resources/interface_w60g_station.md ================================================ # routeros_interface_w60g_station (Resource) ## Example Usage ```terraform resource "routeros_interface_w60g_station" "test" { name = "wlan60-station-1" parent = "wlan60-1" remote-address = "AA:AA:AA:AA:AA:AA" put-in-bridge = "parent" } ``` ## Schema ### Required - `mac_address` (String) MAC address of the station interface. - `name` (String) Name of the interface. - `parent` (String) Parent interface name. ### Optional - `arp` (String) Address Resolution Protocol mode: * disabled - the interface will not use ARP * enabled - the interface will use ARP * local-proxy-arp - the router performs proxy ARP on the interface and sends replies to the same interface * proxy-arp - the router performs proxy ARP on the interface and sends replies to other interfaces * reply-only - the interface will only reply to requests originated from matching IP address/MAC address combinations which are entered as static entries in the ARP table. No dynamic entries will be automatically stored in the ARP table. Therefore for communications to be successful, a valid static entry must already exist. - `arp_timeout` (String) ARP timeout is time how long ARP record is kept in ARP table after no packets are received from IP. Value auto equals to the value of arp-timeout in IP/Settings, default is 30s. Can use postfix `ms`, `s`, `m`, `h`, `d` for milliseconds, seconds, minutes, hours or days. If no postfix is set then seconds (s) is used. - `disabled` (Boolean) - `mtu` (String) Layer3 Maximum transmission unit ('auto', 0 .. 65535). Look for the exact minimum value in the MikroTik documentation - `put_in_bridge` (String) Add station device interface to specific bridge. - `remote_address` (String) MAC address of bridge interface, station is connecting to. ### Read-Only - `id` (String) The ID of this resource. - `running` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/w60g/station get [print show-ids]] terraform import routeros_interface_w60g_station.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_w60g_station.test "name=xxx" ``` ================================================ FILE: docs/resources/interface_wireguard.md ================================================ # routeros_interface_wireguard (Resource) ## Example Usage ```terraform resource "routeros_interface_wireguard" "test_wg_interface" { name = "test_wg_interface" listen_port = "13231" } ``` ## Schema ### Required - `listen_port` (Number) Port for WireGuard service to listen on for incoming sessions. - `name` (String) Changing the name of this resource will force it to be recreated. > The links of other configuration properties to this resource may be lost! > Changing the name of the resource outside of a Terraform will result in a loss of control integrity for that resource! ### Optional - `comment` (String) - `disabled` (Boolean) - `mtu` (String) Layer3 Maximum transmission unit ('auto', 0 .. 65535). Look for the exact minimum value in the MikroTik documentation - `private_key` (String, Sensitive) A base64 private key. If not specified, it will be automatically generated upon interface creation. ### Read-Only - `id` (String) The ID of this resource. - `public_key` (String) A base64 public key is calculated from the private key. - `running` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wireguard get [print show-ids]] terraform import routeros_interface_wireguard.test_wg_interface "*1" #Or you can import a resource using one of its attributes terraform import routeros_interface_wireguard.test_wg_interface "name=xxx" ``` ================================================ FILE: docs/resources/interface_wireguard_peer.md ================================================ # routeros_interface_wireguard_peer (Resource) ## Example Usage ```terraform resource "routeros_interface_wireguard" "test_wg_interface" { name = "test_wg_interface" listen_port = "13231" } resource "routeros_interface_wireguard_peer" "wg_peer" { interface = routeros_interface_wireguard.test_wg_interface.name public_key = "MY_BASE_64_PUBLIC_KEY" allowed_address = [ "192.168.0.0/16", "172.16.0.0/12", "10.0.0.0/8", ] } ``` ## Schema ### Required - `allowed_address` (List of String) List of IP (v4 or v6) addresses with CIDR masks from which incoming traffic for this peer is allowed and to which outgoing traffic for this peer is directed. The catch-all 0.0.0.0/0 may be specified for matching all IPv4 addresses, and ::/0 may be specified for matching all IPv6 addresses. - `interface` (String) Name of the interface. - `public_key` (String) The remote peer's calculated public key. ### Optional - `client_address` (String) When imported using a qr code for a client (for example, a phone), then this address for the wg interface is set on that device. - `client_dns` (String) Specify when using WireGuard Server as a VPN gateway for peer traffic. - `client_endpoint` (String) The IP address and port number of the WireGuard Server. - `client_keepalive` (String) Same as persistent-keepalive but from peer side. - `client_listen_port` (Number) The local port upon which this WireGuard tunnel will listen for incoming traffic from peers, and the port from which it will source outgoing packets. - `comment` (String) - `disabled` (Boolean) - `endpoint_address` (String) An endpoint IP or hostname can be left blank to allow remote connection from any address. - `endpoint_port` (String) An endpoint port can be left blank to allow remote connection from any port. - `is_responder` (Boolean) Specifies if peer is intended to be connection initiator or only responder. Should be used on WireGuard devices that are used as `servers` for other devices as clients to connect to. Otherwise router will all repeatedly try to connect `endpoint-address` or `current-endpoint-address` causing unnecessary system logs to be written. - `name` (String) Name of the tunnel. - `persistent_keepalive` (String) A seconds interval, between 1 and 65535 inclusive, of how often to send an authenticated empty packet to the peer for the purpose of keeping a stateful firewall or NAT mapping valid persistently. For example, if the interface very rarely sends traffic, but it might at anytime receive traffic from a peer, and it is behind NAT, the interface might benefit from having a persistent keepalive interval of 25 seconds. - `preshared_key` (String, Sensitive) A **base64** preshared key. Optional, and may be omitted. This option adds an additional layer of symmetric-key cryptography to be mixed into the already existing public-key cryptography, for post-quantum resistance. - `private_key` (String) A base64 private key. If not specified, it will be automatically generated upon interface creation. ### Read-Only - `current_endpoint_address` (String) The most recent source IP address of correctly authenticated packets from the peer. - `current_endpoint_port` (Number) The most recent source IP port of correctly authenticated packets from the peer. - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. - `last_handshake` (String) Time in seconds after the last successful handshake. - `rx` (String) The total amount of bytes received from the peer. - `tx` (String) The total amount of bytes transmitted to the peer. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wireguard/peers get [print show-ids]] terraform import routeros_interface_wireguard_peer.wg_peer "*0" #Or you can import a resource using one of its attributes terraform import routeros_interface_wireguard_peer.wg_peer "name=xxx" ``` ================================================ FILE: docs/resources/interface_wireless.md ================================================ # routeros_interface_wireless (Resource) ## Example Usage ```terraform variable "wlan_2ghz_disabled" { type = bool default = false } resource "routeros_interface_wireless" "wlan-2ghz" { name = "wlan1" disabled = var.wlan_2ghz_disabled } resource "routeros_interface_wireless_security_profiles" "test" { name = "test-profile" mode = "dynamic-keys" authentication_types = ["wpa-psk", "wpa2-psk"] wpa_pre_shared_key = "wpa_psk_key" wpa2_pre_shared_key = "wpa2_psk_key" } resource "routeros_interface_wireless" "test" { depends_on = [resource.routeros_interface_wireless_security_profiles.test] security_profile = resource.routeros_interface_wireless_security_profiles.test.name mode = "ap-bridge" master_interface = resource.routeros_interface_wireless.wlan-2ghz.name name = "wlan-guest" ssid = "guests" basic_rates_ag = ["6Mbps", "9Mbps"] } ``` ## Schema ### Required - `name` (String) Name of the interface. ### Optional - `adaptive_noise_immunity` (String) This property is only effective for cards based on Atheros chipset. - `allow_sharedkey` (Boolean) Allow WEP Shared Key clients to connect. Note that no authentication is done for these clients (WEP Shared keys are not compared to anything) - they are just accepted at once (if access list allows that). - `ampdu_priorities` (Set of Number) Frame priorities for which AMPDU sending (aggregating frames and sending using block acknowledgment) should get negotiated and used. Using AMPDUs will increase throughput, but may increase latency, therefore, may not be desirable for real-time traffic (voice, video). Due to this, by default AMPDUs are enabled only for best-effort traffic. - `amsdu_limit` (Number) Max AMSDU that device is allowed to prepare when negotiated. AMSDU aggregation may significantly increase throughput especially for small frames, but may increase latency in case of packet loss due to retransmission of aggregated frame. Sending and receiving AMSDUs will also increase CPU usage. - `amsdu_threshold` (Number) Max frame size to allow including in AMSDU. - `antenna_gain` (Number) Antenna gain in dBi, used to calculate maximum transmit power according to country regulations. - `antenna_mode` (String) Select antenna to use for transmitting and for receiving: `ant-a` - use only 'a'; antenna `ant-b` - use only 'b'; antenna `txa-rxb` - use antenna 'a' for transmitting, antenna 'b' for receiving; `rxa-txb` - use antenna 'b' for transmitting, antenna 'a' for receiving. - `area` (String) Identifies group of wireless networks. This value is announced by AP, and can be matched in connect-list by area-prefix. This is a proprietary extension. - `arp` (String) ARP Mode. - `arp_timeout` (String) ARP timeout is time how long ARP record is kept in ARP table after no packets are received from IP. Value auto equals to the value of arp-timeout in `/ip settings`, default is 30s. - `band` (String) Defines set of used data rates, channel frequencies and widths. - `basic_rates_ag` (Set of String) Similar to the basic-rates-b property, but used for 5ghz, 5ghz-10mhz, 5ghz-5mhz, 5ghz-turbo, 2.4ghz-b/g, 2.4ghz-onlyg, 2ghz-10mhz, 2ghz-5mhz and 2.4ghz-g-turbo bands. - `basic_rates_b` (Set of String) List of basic rates, used for `2.4ghz-b`, `2.4ghz-b/g` and `2.4ghz-onlyg` bands.Client will connect to AP only if it supports all basic rates announced by the AP. AP will establish WDS link only if it supports all basic rates of the other AP.This property has effect only in AP modes, and when value of rate-set is configured. - `bridge_mode` (String) Allows to use station-bridge mode. - `burst_time` (String) Time in microseconds which will be used to send data without stopping. Note that no other wireless cards in that network will be able to transmit data during burst-time microseconds. This setting is available only for AR5000, AR5001X, and AR5001X+ chipset based cards. - `channel_width` (String) Use of extension channels (e.g. `C`e, `eC` etc) allows additional 20MHz extension channels and if it should be located below or above the control (main) channel. Extension channel allows 802.11n devices to use up to 40MHz (802.11ac up to 160MHz) of spectrum in total thus increasing max throughput. Channel widths with `XX` and `XXXX` extensions automatically scan for a less crowded control channel frequency based on the number of concurrent devices running in every frequency and chooses the `C` - Control channel frequency automatically. - `comment` (String) - `compression` (Boolean) Setting this property to yes will allow the use of the hardware compression. Wireless interface must have support for hardware compression. Connections with devices that do not use compression will still work. - `country` (String) Limits available bands, frequencies and maximum transmit power for each frequency. Also specifies default value of scan-list. Value no_country_set is an FCC compliant set of channels. - `default_ap_tx_limit` (Number) This is the value of ap-tx-limit for clients that do not match any entry in the access-list. 0 means no limit. - `default_authentication` (Boolean) For AP mode, this is the value of authentication for clients that do not match any entry in the access-list. For station mode, this is the value of connect for APs that do not match any entry in the connect-list. - `default_client_tx_limit` (Number) This is the value of `client-tx-limit` for clients that do not match any entry in the access-list. 0 means no limit. - `default_forwarding` (Boolean) This is the value of forwarding for clients that do not match any entry in the access-list. - `disable_running_check` (Boolean) When set to yes interface will always have running flag. If value is set to no', the router determines whether the card is up and running - for AP one or more clients have to be registered to it, for station, it should be connected to an AP. - `disabled` (Boolean) - `disconnect_timeout` (String) This interval is measured from third sending failure on the lowest data rate. At this point `3 * (hw-retries + 1)` frame transmits on the lowest data rate had failed. During disconnect-timeout packet transmission will be retried with on-fail-retry-time interval. If no frame can be transmitted successfully during disconnect-timeout, the connection is closed, and this event is logged as `extensive data loss`. Successful frame transmission resets this timer. - `distance` (String) How long to wait for confirmation of unicast frames (ACKs) before considering transmission unsuccessful, or in short ACK-Timeout. Distance value has these behaviors: * Dynamic - causes AP to detect and use the smallest timeout that works with all connected clients. * Indoor - uses the default ACK timeout value that the hardware chip manufacturer has set. * Number - uses the input value in formula: `ACK-timeout = ((distance * 1000) + 299) / 300 us` Acknowledgments are not used in Nstreme/NV2 protocols. - `frame_lifetime` (Number) Discard frames that have been queued for sending longer than frame-lifetime. By default, when value of this property is 0, frames are discarded only after connection is closed. - `frequency` (String) Channel frequency value in MHz on which AP will operate. Allowed values depend on the selected band, and are restricted by country setting and wireless card capabilities. This setting has no effect if interface is in any of station modes, or in wds-slave mode, or if DFS is active.Note: If using mode `superchannel`. - `frequency_mode` (String) Three frequency modes are available: * regulatory-domain - Limit available channels and maximum transmit power for each channel according to the value of country * manual-txpower - Same as above, but do not limit maximum transmit power *`superchannel` - Conformance Testing Mode. Allow all channels supported by the card. List of available channels for each band can be seen in `/interface wireless` info allowed-channels. This mode allows you to test wireless channels outside the default scan-list and/or regulatory domain. This mode should only be used in controlled environments, or if you have special permission to use it in your region. Before v4.3 this was called Custom Frequency Upgrade, or Superchannel. Since RouterOS v4.3 this mode is available without special key upgrades to all installations. - `frequency_offset` (Number) Allows to specify offset if the used wireless card operates at a different frequency than is shown in RouterOS, in case a frequency converter is used in the card. So if your card works at 4000MHz but RouterOS shows 5000MHz, set offset to 1000MHz and it will be displayed correctly. The value is in MHz and can be positive or negative. - `guard_interval` (String) Whether to allow use of short guard interval (refer to 802.11n MCS specification to see how this may affect throughput). `any` will use either short or long, depending on data rate, `long` will use long. - `hide_ssid` (Boolean) `true` - AP does not include SSID in the beacon frames, and does not reply to probe requests that have broadcast SSID. `false` - AP includes SSID in the beacon frames, and replies to probe requests that have broadcast SSID.This property has an effect only in AP mode. Setting it to yes can remove this network from the list of wireless networks that are shown by some client software. Changing this setting does not improve the security of the wireless network, because SSID is included in other frames sent by the AP. - `ht_basic_mcs` (Set of String) Modulation and Coding Schemes that every connecting client must support. Refer to 802.11n for MCS specification. - `ht_supported_mcs` (Set of String) Modulation and Coding Schemes that this device advertises as supported. Refer to 802.11n for MCS specification. - `hw_fragmentation_threshold` (String) Specifies maximum fragment size in bytes when transmitted over the wireless medium. 802.11 standard packet (MSDU in 802.11 terminologies) fragmentation allows packets to be fragmented before transmitting over a wireless medium to increase the probability of successful transmission (only fragments that did not transmit correctly are retransmitted). Note that transmission of a fragmented packet is less efficient than transmitting unfragmented packet because of protocol overhead and increased resource usage at both - transmitting and receiving party. - `hw_protection_mode` (String) Frame protection support property. - `hw_protection_threshold` (Number) Frame protection support property read more >>. - `hw_retries` (Number) Number of times sending frame is retried without considering it a transmission failure. Data-rate is decreased upon failure and the frame is sent again. Three sequential failures on the lowest supported rate suspend transmission to this destination for the duration of on-fail-retry-time. After that, the frame is sent again. The frame is being retransmitted until transmission success, or until the client is disconnected after disconnect-timeout. The frame can be discarded during this time if frame-lifetime is exceeded. - `installation` (String) Adjusts scan-list to use indoor, outdoor or all frequencies for the country that is set. - `interworking_profile` (String) - `keepalive_frames` (String) Applies only if wireless interface is in `mode = ap-bridge`. If a client has not communicated for around 20 seconds, AP sends a `keepalive-frame`. Note, disabling the feature can lead to `ghost` clients in registration-table. - `l2mtu` (Number) Layer2 Maximum transmission unit. [See](https://wiki.mikrotik.com/wiki/Maximum_Transmission_Unit_on_RouterBoards). - `mac_address` (String) MAC address. - `master_interface` (String) Name of wireless interface that has virtual-ap capability. Virtual AP interface will only work if master interface is in ap-bridge, bridge, station or wds-slave mode. This property is only for virtual AP interfaces. - `max_station_count` (Number) Maximum number of associated clients. WDS links also count toward this limit. - `mode` (String) Selection between different station and access point (AP) modes. * Station modes: `station` - Basic station mode. Find and connect to acceptable AP. `station-wds` - Same as station, but create WDS link with AP, using proprietary extension. AP configuration has to allow WDS links with this device. Note that this mode does not use entries in wds. `station-pseudobridge` - Same as station, but additionally perform MAC address translation of all traffic. Allows interface to be bridged. `station-pseudobridge-clone` - Same as station-pseudobridge, but use station-bridge-clone-mac address to connect to AP. `station-bridge` - Provides support for transparent protocol-independent L2 bridging on the station device. RouterOS AP accepts clients in station-bridge mode when enabled using bridge-mode parameter. In this mode, the AP maintains a forwarding table with information on which MAC addresses are reachable over which station device. Only works with RouterOS APs. With station-bridge mode, it is not possible to connect to CAPsMAN controlled CAP. * AP modes: `ap-bridge` - Basic access point mode. `bridge` - Same as ap-bridge, but limited to one associated client. `wds-slave` - Same as ap-bridge, but scan for AP with the same ssid and establishes WDS link. If this link is lost or cannot be established, then continue scanning. If dfs-mode is radar-detect, then APs with enabled hide-ssid will not be found during scanning. * Special modes: `alignment-only` - Put the interface in a continuous transmit mode that is used for aiming the remote antenna. `nstreme-dual-slave` - allow this interface to be used in nstreme-dual setup. MAC address translation in pseudobridge modes works by inspecting packets and building a table of corresponding IP and MAC addresses. All packets are sent to AP with the MAC address used by pseudobridge, and MAC addresses of received packets are restored from the address translation table. There is a single entry in the address translation table for all non-IP packets, hence more than one host in the bridged network cannot reliably use non-IP protocols. Note: Currently IPv6 doesn't work over Pseudobridge. - `mtu` (String) Layer3 Maximum transmission unit ('auto', 0 .. 65535). Look for the exact minimum value in the MikroTik documentation - `multicast_buffering` (String) For a client that has power saving, buffer multicast packets until next beacon time. A client should wake up to receive a beacon, by receiving beacon it sees that there are multicast packets pending, and it should wait for multicast packets to be sent. - `multicast_helper` (String) When set to full, multicast packets will be sent with a unicast destination MAC address, resolving multicast problem on the wireless link. This option should be enabled only on the access point, clients should be configured in station-bridge mode. Available starting from v5.15.disabled - disables the helper and sends multicast packets with multicast destination MAC addressesdhcp - dhcp packet mac addresses are changed to unicast mac addresses prior to sending them outfull - all multicast packet mac address are changed to unicast mac addresses prior to sending them outdefault - default choice that currently is set to dhcp. Value can be changed in future releases. - `noise_floor_threshold` (String) For advanced use only, as it can badly affect the performance of the interface. It is possible to manually set noise floor threshold value. By default, it is dynamically calculated. This property also affects received signal strength. This property is only effective on non-AC chips. - `nv2_cell_radius` (Number) Setting affects the size of contention time slot that AP allocates for clients to initiate connection and also size of time slots used for estimating distance to client. When setting is too small, clients that are farther away may have trouble connecting and/or disconnect with `ranging timeout` error. Although during normal operation the effect of this setting should be negligible, in order to maintain maximum performance, it is advised to not increase this setting if not necessary, so AP is not reserving time that is actually never used, but instead allocates it for actual data transfer.on AP: distance to farthest client in kmon station: no effect. - `nv2_downlink_ratio` (Number) Specifies the Nv2 downlink ratio. Uplink ratio is automatically calculated from the downlink-ratio value. When using dynamic-downlink mode the downlink-ratio is also used when link get fully saturated. Minimum value is 20 and maximum 80. - `nv2_mode` (String) Specifies to use dynamic or fixed downlink/uplink ratio. - `nv2_noise_floor_offset` (String) - `nv2_preshared_key` (String, Sensitive) Specifies preshared key to be used. - `nv2_qos` (String) Sets the packet priority mechanism, firstly data from high priority queue is sent, then lower queue priority data until 0 queue priority is reached. When link is full with high priority queue data, lower priority data is not sent. Use it very carefully, setting works on APframe-priority - manual setting that can be tuned with Mangle rules.default - default setting where small packets receive priority for best latency. - `nv2_queue_count` (Number) Specifies how many priority queues are used in Nv2 network. - `nv2_security` (String) Specifies Nv2 security mode. - `nv2_sync_secret` (String, Sensitive) Specifies secret key for use in the Nv2 synchronization. Secret should match on Master and Slave devices in order to establish the synced state. - `on_fail_retry_time` (String) After third sending failure on the lowest data rate, wait for specified time interval before retrying. - `periodic_calibration` (String) Setting default enables periodic calibration if info default-periodic-calibration property is enabled. Value of that property depends on the type of wireless card. This property is only effective for cards based on Atheros chipset. - `periodic_calibration_interval` (Number) This property is only effective for cards based on Atheros chipset. - `preamble_mode` (String) Short preamble mode is an option of 802.11b standard that reduces per-frame overhead.On AP: * long - Do not use short preamble. * short - Announce short preamble capability. Do not accept connections from clients that do not have this capability. * both - Announce short preamble capability. On station: *long - do not use short preamble. * short - do not connect to AP if it does not support short preamble. * both - Use short preamble if AP supports it. - `prism_cardtype` (String) Specify type of the installed Prism wireless card. - `proprietary_extensions` (String) RouterOS includes proprietary information in an information element of management frames. This parameter controls how this information is included. `pre-2.9.25` - This is older method. It can interoperate with newer versions of RouterOS. This method is incompatible with some clients, for example, Centrino based ones. `post-2.9.25` - This uses standardized way of including vendor specific information, that is compatible with newer wireless clients. - `rate_selection` (String) Starting from v5.9 default value is advanced since legacy mode was inefficient. - `rate_set` (String) Two options are available: `default` - default basic and supported rate sets are used. Values from basic-rates and supported-rates parameters have no effect. `configured` - use values from basic-rates, supported-rates, basic-mcs, mcs. - `rx_chains` (Set of Number) Which antennas to use for receive. In current MikroTik routers, both RX and TX chain must be enabled, for the chain to be enabled. - `scan_list` (String) The default value is all channels from selected band that are supported by card and allowed by the country and frequency-mode settings (this list can be seen in info). For default scan list in 5ghz band channels are taken with 20MHz step, in 5ghz-turbo band - with 40MHz step, for all other bands - with 5MHz step. If scan-list is specified manually, then all matching channels are taken. (Example: scan-list=default,5200-5245,2412-2427 - This will use the default value of scan list for current band, and add to it supported frequencies from 5200-5245 or 2412-2427 range.) Since RouterOS v6.0 with Winbox or Webfig, for inputting of multiple frequencies, add each frequency or range of frequencies into separate multiple scan-lists. Using a comma to separate frequencies is no longer supported in Winbox/Webfig since v6.0.Since RouterOS v6.35 (wireless-rep) scan-list support step feature where it is possible to manually specify the scan step. Example: scan-list=5500-5600:20 will generate such scan-list values 5500,5520,5540,5560,5580,5600. - `secondary_frequency` (String) Specifies secondary channel, required to enable 80+80MHz transmission. To disable 80+80MHz functionality, set secondary-frequency to `` or unset the value via CLI/GUI. - `security_profile` (String) Name of profile from security-profiles. - `skip_dfs_channels` (String) These values are used to skip all DFS channels or specifically skip DFS CAC channels in range 5600-5650MHz which detection could go up to 10min. - `ssid` (String) SSID (service set identifier) is a name that identifies wireless network. - `station_bridge_clone_mac` (String) This property has effect only in the station-pseudobridge-clone mode.Use this MAC address when connection to AP. If this value is 00:00:00:00:00:00, station will initially use MAC address of the wireless interface.As soon as packet with MAC address of another device needs to be transmitted, station will reconnect to AP using that address. - `station_roaming` (String) Station Roaming feature is available only for 802.11 wireless protocol and only for station modes. - `supported_rates_ag` (String) List of supported rates, used for all bands except 2ghz-b. - `supported_rates_b` (String) List of supported rates, used for `2ghz-b, `2ghz-b/g` and `2ghz-b/g/n` bands. Two devices will communicate only using rates that are supported by both devices. This property has effect only when value of rate-set is configured. - `tdma_period_size` (Number) Specifies TDMA period in milliseconds. It could help on the longer distance links, it could slightly increase bandwidth, while latency is increased too. - `tx_chains` (Set of Number) Which antennas to use for transmitting. In current MikroTik routers, both RX and TX chain must be enabled, for the chain to be enabled. - `tx_power` (Number) For 802.11ac wireless interface it's total power but for 802.11a/b/g/n it's power per chain. - `tx_power_mode` (String) Sets up tx-power mode for wireless card `default` - use values stored in the card `all-rates-fixed` - use same transmit power for all data rates. Can damage the card if transmit power is set above rated value of the card for used rate. `manual-table` - define transmit power for each rate separately. Can damage the card if transmit power is set above rated value of the card for used rate. `card-rates` - use transmit power calculated for each rate based on value of tx-power parameter. Legacy mode only compatible with currently discontinued products. - `update_stats_interval` (String) How often to request update of signals strength and ccq values from clients. Access to registration-table also triggers update of these values.This is proprietary extension. - `vht_basic_mcs` (String) Modulation and Coding Schemes that every connecting client must support. Refer to 802.11ac for MCS specification. You can set MCS interval for each of Spatial Stream * none - will not use selected; * mcs0-7 - client must support MCS-0 to MCS-7; * mcs0-8 - client must support MCS-0 to MCS-8; * mcs0-9 - client must support MCS-0 to MCS-9. - `vht_supported_mcs` (String) Modulation and Coding Schemes that this device advertises as supported. Refer to 802.11ac for MCS specification. You can set MCS interval for each of Spatial Stream * none - will not use selected; * mcs0-7 - devices will advertise as supported MCS-0 to MCS-7; * mcs0-8 - devices will advertise as supported MCS-0 to MCS-8; * mcs0-9 - devices will advertise as supported MCS-0 to MCS-9. - `vlan_id` (Number) VLAN ID to use if doing VLAN tagging. - `vlan_mode` (String) VLAN tagging mode specifies if traffic coming from client should get tagged (and untagged when going to client). - `wds_cost_range` (String) Bridge port cost of WDS links are automatically adjusted, depending on measured link throughput. Port cost is recalculated and adjusted every 5 seconds if it has changed by more than 10%, or if more than 20 seconds have passed since the last adjustment.Setting this property to 0 disables automatic cost adjustment.Automatic adjustment does not work for WDS links that are manually configured as a bridge port. - `wds_default_bridge` (String) When WDS link is established and status of the wds interface becomes running, it will be added as a bridge port to the bridge interface specified by this property. When WDS link is lost, wds interface is removed from the bridge. If wds interface is already included in a bridge setup when WDS link becomes active, it will not be added to bridge specified by , and will (needs editing). - `wds_default_cost` (Number) Initial bridge port cost of the WDS links. - `wds_ignore_ssid` (Boolean) By default, WDS link between two APs can be created only when they work on the same frequency and have the same SSID value. If this property is set to yes, then SSID of the remote AP will not be checked. This property has no effect on connections from clients in station-wds mode. It also does not work if wds-mode is static-mesh or dynamic-mesh. - `wds_mode` (String) Controls how WDS links with other devices (APs and clients in station-wds mode) are established. * disabled does not allow WDS links. * static only allows WDS links that are manually configured in WDS. * dynamic also allows WDS links with devices that are not configured in WDS, by creating required entries dynamically. Such dynamic WDS entries are removed automatically after the connection with the other AP is lost. * -mesh modes use different (better) method for establishing link between AP, that is not compatible with APs in non-mesh mode. This method avoids one-sided WDS links that are created only by one of the two APs. Such links cannot pass any data.When AP or station is establishing WDS connection with another AP, it uses connect-list to check whether this connection is allowed. If station in station-wds mode is establishing connection with AP, AP uses access-list to check whether this connection is allowed.If mode is station-wds, then this property has no effect. - `wireless_protocol` (String) Specifies protocol used on wireless interface; * unspecified - protocol mode used on previous RouterOS versions (v3.x, v4.x). Nstreme is enabled by old enable-nstreme setting, Nv2 configuration is not possible. * any : on AP - regular 802.11 Access Point or Nstreme Access Point; on station - selects Access Point without specific sequence, it could be changed by connect-list rules. * nstreme - enables Nstreme protocol (the same as old enable-nstreme setting). * nv2 - enables Nv2 protocol. * nv2 nstreme : on AP - uses first wireless-protocol setting, always Nv2; on station - searches for Nv2 Access Point, then for Nstreme Access Point. * nv2 nstreme 802.11 - on AP - uses first wireless-protocol setting, always Nv2; on station - searches for Nv2 Access Point, then for Nstreme Access Point, then for regular 802.11 Access Point.Warning! Nv2 doesn't have support for Virtual AP. - `wmm_support` (String) Specifies whether to enable WMM. Only applies to bands `B` and `G`. Other bands will have it enabled regardless of this setting. - `wps_mode` (String) WPS Server allows to connect wireless clients that support WPS to AP protected with the Pre-Shared Key without specifying that key in the clients configuration. ### Read-Only - `default_name` (String) - `id` (String) The ID of this resource. - `interface_type` (String) - `radio_name` (String) Descriptive name of the device, that is shown in registration table entries on the remote devices. This is a proprietary extension. - `running` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wireless get [print show-ids]] terraform import routeros_interface_wireless.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_wireless.test "name=xxx" ``` ================================================ FILE: docs/resources/interface_wireless_access_list.md ================================================ # routeros_interface_wireless_access_list (Resource) ## Example Usage ```terraform resource "routeros_interface_wireless_access_list" "test" { signal_range = "-100..100" time = "3h3m-5h,mon,tue,wed,thu,fri" mac_address = "00:AA:BB:CC:DD:EE" } ``` ## Schema ### Optional - `allow_signal_out_of_range` (String) Option which permits client's signal to be out of the range always or for some time interval. - `ap_tx_limit` (Number) Limit rate of data transmission to this client. Value 0 means no limit. Value is in bits per second. - `authentication` (Boolean) No - Client association will always fail.yes - Use authentication procedure that is specified in the security-profile of the interface. - `client_tx_limit` (Number) Ask client to limit rate of data transmission. Value 0 means no limit.This is a proprietary extension that is supported by RouterOS clients.Value is in bits per second. - `comment` (String) - `disabled` (Boolean) - `forwarding` (Boolean) * false - Client cannot send frames to other station that are connected to same access point. *true - Client can send frames to other stations on the same access point. - `interface` (String) Rules with interface=any are used for any wireless interface and the `interface = all` defines interface-list `all` name. To make rule that applies only to one wireless interface, specify that interface as a value of this property. - `mac_address` (String) Rule matches client with the specified MAC address. Value 00:00:00:00:00:00 matches always. - `management_protection_key` (String) Management protection shared secret. - `private_algo` (String) Only for `WEP` modes. - `private_key` (String) Only for `WEP` modes (HEX). - `private_pre_shared_key` (String) Used in `WPA PSK` mode. - `signal_range` (String) Rule matches if signal strength of the station is within the range.If signal strength of the station will go out of the range that is specified in the rule, access point will disconnect that station. - `time` (String) Rule will match only during specified time.Station will be disconnected after specified time ends. Both start and end time is expressed as time since midnight, 00:00. Rule will match only during specified days of the week. Ex: "3h3m-5h,mon,tue,wed,thu,fri" - `vlan_id` (Number) VLAN ID to use if doing VLAN tagging. - `vlan_mode` (String) VLAN tagging mode specifies if traffic coming from client should get tagged (and untagged when going to client). ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wireless/access-list get [print show-ids]] terraform import routeros_interface_wireless_access_list.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_wireless_access_list.test "name=xxx" ``` ================================================ FILE: docs/resources/interface_wireless_cap.md ================================================ # routeros_interface_wireless_cap (Resource) ## Example Usage ```terraform resource "routeros_interface_wireless_cap" "settings" { discovery_interfaces = ["bridge1"] enabled = true interfaces = ["wlan1", "wlan2"] } ``` ## Schema ### Optional - `bridge` (String) Bridge interface to add the interface as a bridge port. - `caps_man_addresses` (List of String) List of Manager IP addresses that CAP will attempt to contact during discovery. - `caps_man_certificate_common_names` (List of String) List of manager certificate common names that CAP will connect to. - `caps_man_names` (List of String) An ordered list of CAPs Manager names that the CAP will connect to. - `certificate` (String) Certificate to use for authentication. - `discovery_interfaces` (Set of String) List of interfaces over which CAP should attempt to discover CAPs Manager. - `enabled` (Boolean) Disable or enable the CAP functionality. - `interfaces` (Set of String) List of interfaces managed by CAPs Manager. - `lock_to_caps_man` (Boolean) Lock CAP to the first CAPsMAN it connects to. - `static_virtual` (Boolean) An option that creates static virtual interfaces. ### Read-Only - `id` (String) The ID of this resource. - `locked_caps_man_common_name` (String) Common name of the CAPsMAN that the CAP is locked to. - `requested_certificate` (String) Requested certificate. ## Import Import is supported using the following syntax: ```shell terraform import routeros_interface_wireless_cap.settings . #Or you can import a resource using one of its attributes terraform import routeros_interface_wireless_cap.settings "name=xxx" ``` ================================================ FILE: docs/resources/interface_wireless_connect_list.md ================================================ # routeros_interface_wireless_connect_list (Resource) ## Example Usage ```terraform resource "routeros_interface_wireless_connect_list" "test" { interface = "wlan0" security_profile = "test-secp" } ``` ## Schema ### Required - `interface` (String) Name of the interface. ### Optional - `allow_signal_out_of_range` (String) - `area_prefix` (String) Rule matches if area value of AP (a proprietary extension) begins with specified value.area value is a proprietary extension. - `comment` (String) - `connect` (Boolean) Available options: yes - Connect to access point that matches this rule. no - Do not connect to any access point that matches this rule. - `disabled` (Boolean) - `interworking` (String) - `iw_asra` (String) Additional Steps Required for Access. Set to yes, if a user should take additional steps to access the internet, like the walled garden. - `iw_authentication_types` (String) This property is only effective when `asra` is set to `yes`. Value of `url` is optional and not needed if `dns-redirection` or `online-enrollment` is selected. To set the value of `url` to empty string use double quotes. For example: `authentication-types=online-enrollment:""` - `iw_connection_capabilities` (String) This option allows to provide information about the allowed IP protocols and ports. This information can be provided in ANQP response. The first number represents the IP protocol number, the second number represents a port number. * closed - set if protocol and port combination is not allowed; * open - set if protocol and port combination is allowed; * unknown - set if protocol and port combination is either open or closed. Example: `connection-capabilities=6:80:open,17:5060:closed`Setting such a value on an Access Point informs the Wireless client, which is connecting to the Access Point, that HTTP (6 - TCP, 80 - HTTP) is allowed and VoIP (17 - UDP; 5060 - VoIP) is not allowed. This property does not restrict or allow usage of these protocols and ports, it only gives information to station device which is connecting to Access Point. - `iw_esr` (String) Emergency services reachable (ESR). Set to yes in order to indicate that emergency services are reachable through the access point. - `iw_hessid` (String) Homogenous extended service set identifier (HESSID). Devices that provide access to same external networks are in one homogenous extended service set. This service set can be identified by HESSID that is the same on all access points in this set. 6-byte value of HESSID is represented as MAC address. It should be globally unique, therefore it is advised to use one of the MAC address of access point in the service set. - `iw_hotspot20` (String) Indicate Hotspot 2.0 capability of the Access Point. - `iw_hotspot20_dgaf` (String) Downstream Group-Addressed Forwarding (DGAF). Sets value of DGAF bit to indicate whether multicast and broadcast frames to clients are disabled or enabled. * yes - multicast and broadcast frames to clients are enabled; * no - multicast and broadcast frames to clients are disabled. To disable multicast and broadcast frames set `multicast-helper=full`. - `iw_internet` (String) Whether the internet is available through this connection or not. This information is included in the Interworking element. - `iw_ipv4_availability` (String) Information about what IPv4 address and access are available. * not-available - Address type not available; * public - public IPv4 address available; * port-restricted - port-restricted IPv4 address available; * single-nated - single NATed private IPv4 address available; * double-nated - double NATed private IPv4 address available; * port-restricted-single-nated -port-restricted IPv4 address and single NATed IPv4 address available; * port-restricted-double-nated - port-restricted IPv4 address and double NATed IPv4 address available; * unknown - availability of the address type is not known. - `iw_ipv6_availability` (String) Information about what IPv6 address and access are available. * not-available - Address type not available; * available - address type available; * unknown - availability of the address type is not known. - `iw_network_type` (String) Information about network access type. * emergency-only - a network dedicated and limited to accessing emergency services; * personal-device - a network of personal devices. An example of this type of network is a camera that is attached to a printer, thereby forming a network for the purpose of printing pictures; * private - network for users with user accounts. Usually used in enterprises for employees, not guests; * private-with-guest - same as private, but guest accounts are available; * public-chargeable - a network that is available to anyone willing to pay. For example, a subscription to Hotspot 2.0 service or in-room internet access in a hotel; * public-free - network is available to anyone without any fee. For example, municipal network in city or airport Hotspot; * test - network used for testing and experimental uses. Not used in production; * wildcard - is used on Wireless clients. Sending probe request with a wildcard as network type value will make all Interworking Access Points respond despite their actual network-type setting. A client sends a probe request frame with network-type set to value it is interested in. It will receive replies only from access points with the same value (except the case of wildcard). - `iw_realms` (String) Information about supported realms and the corresponding EAP method. `realms=example.com:eap-tls,foo.ba:not-specified` - `iw_roaming_ois` (String) Organization identifier (OI) usually are 24-bit is unique identifiers like organizationally unique identifier (OUI) or company identifier (CID). In some cases, OI is longer for example OUI-36.A subscription service provider (SSP) can be specified by its OI. roaming-ois property can contain zero or more SSPs OIs whose networks are accessible via this AP. Length of OI should be specified before OI itself. For example, to set E4-8D-8C and 6C-3B-6B: `roaming-ois=03E48D8C036C3B6B` - `iw_uesa` (String) Unauthenticated emergency service accessible (UESA). * no - indicates that no unauthenticated emergency services are reachable through this Access Point; * yes - indicates that higher layer unauthenticated emergency services are reachable through this Access Point. - `iw_venue` (String) Specify the venue in which the Access Point is located. Choose the value from available ones. Some examples: ``` venue=business-bank venue=mercantile-shopping-mall venue=educational-university-or-college ``` - `mac_address` (String) Rule matches only AP with the specified MAC address. - `security_profile` (String) Name of security profile that is used when connecting to matching access points, If value of this property is none, then security profile specified in the interface configuration will be used. In station mode, rule will match only access points that can support specified security profile. Value none will match access point that supports security profile that is specified in the interface configuration. In access point mode value of this property will not be used to match remote devices. - `signal_range` (String) Rule matches if signal strength of the access point is within the range. If station establishes connection to access point that is matched by this rule, it will disconnect from that access point when signal strength goes out of the specified range. - `ssid` (String) Rule matches access points that have this SSID. Empty value matches any SSID. This property has effect only when station mode interface ssid is empty, or when access point mode interface has wds-ignore-ssid=yes. - `three_gpp` (String) - `wireless_protocol` (String) ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wireless/connect-list get [print show-ids]] terraform import routeros_interface_wireless_connect_list.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_wireless_connect_list.test "name=xxx" ``` ================================================ FILE: docs/resources/interface_wireless_security_profiles.md ================================================ # routeros_interface_wireless_security_profiles (Resource) ## Example Usage ```terraform resource "routeros_interface_wireless_security_profiles" "test" { name = "test-profile" mode = "dynamic-keys" authentication_types = ["wpa-psk", "wpa2-psk"] wpa_pre_shared_key = "wpa_psk_key" wpa2_pre_shared_key = "wpa2_psk_key" } ``` ## Schema ### Required - `name` (String) Name of the security profile. ### Optional - `authentication_types` (Set of String) Set of supported authentication types, multiple values can be selected. Access Point will advertise supported authentication types, and client will connect to Access Point only if it supports any of the advertised authentication types. - `comment` (String) - `disable_pmkid` (Boolean) Whether to include `PMKID` into the `EAPOL` frame sent out by the Access Point. Disabling PMKID can cause compatibility issues with devices that use the PMKID to connect to an Access Point. `yes` - removes PMKID from EAPOL frames (improves security, reduces compatibility). `no` - includes PMKID into EAPOL frames (reduces security, improves compatibility).This property only has effect on Access Points. - `eap_methods` (String) Allowed types of authentication methods, multiple values can be selected. This property only has effect on Access Points. `eap-tls` - Use built-in EAP TLS authentication. Both client and server certificates are supported. See description of tls-mode and tls-certificate properties. `eap-ttls-mschapv2` - Use EAP-TTLS with MS-CHAPv2 authentication. `passthrough` - Access Point will relay authentication process to the RADIUS server. `peap` - Use Protected EAP authentication. - `group_ciphers` (String) Access Point advertises one of these ciphers, multiple values can be selected. Access Point uses it to encrypt all broadcast and multicast frames. Client attempts connection only to Access Points that use one of the specified group ciphers. `tkip` - Temporal Key Integrity Protocol - encryption protocol, compatible with legacy WEP equipment, but enhanced to correct some of the WEP flaws. `aes-ccm` - more secure WPA encryption protocol, based on the reliable AES (Advanced Encryption Standard). Networks free of WEP legacy should use only this cipher. - `group_key_update` (String) Controls how often Access Point updates the group key. This key is used to encrypt all broadcast and multicast frames. property only has effect for Access Points. - `interim_update` (String) When RADIUS accounting is used, Access Point periodically sends accounting information updates to the RADIUS server. This property specifies default update interval that can be overridden by the RADIUS server using Acct-Interim-Interval attribute. - `management_protection` (String) Management frame protection. Used for: Deauthentication attack prevention, MAC address cloning issue. Possible values are: `disabled` - management protection is disabled (default), `allowed` - use management protection if supported by remote party (for AP - allow both, non-management protection and management protection clients, for client - connect both to APs with and without management protection), `required` - establish association only with remote devices that support management protection (for AP - accept only clients that support management protection, for client - connect only to APs that support management protection). - `management_protection_key` (String, Sensitive) Management protection shared secret. When interface is in AP mode, default management protection key (configured in security-profile) can be overridden by key specified in access-list or RADIUS attribute. - `mode` (String) Encryption mode for the security profile. `none` - Encryption is not used. Encrypted frames are not accepted. `static-keys-required` - WEP mode. Do not accept and do not send unencrypted frames. Station in static-keys-required mode will not connect to an Access Point in static-keys-optional mode. `static-keys-optional` - WEP mode. Support encryption and decryption, but allow also to receive and send unencrypted frames. Device will send unencrypted frames if encryption algorithm is specified as none. Station in static-keys-optional mode will not connect to an Access Point in `static-keys-required` mode. See also: static-sta-private-algo, static-transmit-key. `dynamic-keys` - WPA mode. - `mschapv2_password` (String) Password to use for authentication when `eap-ttls-mschapv2` or `peap` authentication method is being used. This property only has effect on Stations. - `mschapv2_username` (String) Username to use for authentication when `eap-ttls-mschapv2` or `peap` authentication method is being used. This property only has effect on Stations. - `radius_called_format` (String) mac | mac:ssid | ssid - `radius_eap_accounting` (Boolean) - `radius_mac_accounting` (Boolean) - `radius_mac_authentication` (Boolean) This property affects the way how Access Point processes clients that are not found in the Access List.no - allow or reject client authentication based on the value of default-authentication property of the Wireless interface.yes - Query RADIUS server using MAC address of client as user name. With this setting the value of default-authentication has no effect. - `radius_mac_caching` (String) If this value is set to time interval, the Access Point will cache RADIUS MAC authentication responses for specified time, and will not contact RADIUS server if matching cache entry already exists. Value disabled will disable cache, Access Point will always contact RADIUS server. - `radius_mac_format` (String) Controls how MAC address of the client is encoded by Access Point in the User-Name attribute of the MAC authentication and MAC accounting RADIUS requests. - `radius_mac_mode` (String) By default Access Point uses an empty password, when sending Access-Request during MAC authentication. When this property is set to `as-username-and-password`, Access Point will use the same value for User-Password attribute as for the User-Name attribute. - `static_algo_0` (String) Encryption algorithm to use with the corresponding key. - `static_algo_1` (String) Encryption algorithm to use with the corresponding key. - `static_algo_2` (String) Encryption algorithm to use with the corresponding key. - `static_algo_3` (String) Encryption algorithm to use with the corresponding key. - `static_key_0` (String, Sensitive) Hexadecimal representation of the key. Length of key must be appropriate for selected algorithm. See the Statically configured WEP keys section. - `static_key_1` (String, Sensitive) Hexadecimal representation of the key. Length of key must be appropriate for selected algorithm. See the Statically configured WEP keys section. - `static_key_2` (String, Sensitive) Hexadecimal representation of the key. Length of key must be appropriate for selected algorithm. See the Statically configured WEP keys section. - `static_key_3` (String, Sensitive) Hexadecimal representation of the key. Length of key must be appropriate for selected algorithm. See the Statically configured WEP keys section. - `static_sta_private_algo` (String) Encryption algorithm to use with station private key. Value none disables use of the private key. This property is only used on Stations. Access Point has to get corresponding value either from private-algo property, or from Mikrotik-Wireless-Enc-Algo attribute. Station private key replaces key 0 for unicast frames. Station will not use private key to decrypt broadcast frames. - `static_sta_private_key` (String, Sensitive) Length of key must be appropriate for selected algorithm, see the Statically configured WEP keys section. This property is used only on Stations. Access Point uses corresponding key either from private-key property, or from Mikrotik-Wireless-Enc-Key attribute. - `static_transmit_key` (String) Access Point will use the specified key to encrypt frames for clients that do not use private key. Access Point will also use this key to encrypt broadcast and multicast frames. Client will use the specified key to encrypt frames if static-sta-private-algo is set to none. If corresponding static-algo-N property has value set to none, then frame will be sent unencrypted (when mode is set to static-keys-optional) or will not be sent at all (when mode is set to static-keys-required). - `supplicant_identity` (String, Sensitive) EAP identity that is sent by client at the beginning of EAP authentication. This value is used as a value for User-Name attribute in RADIUS messages sent by RADIUS EAP accounting and RADIUS EAP pass-through authentication. - `tls_certificate` (String) Access Point always needs a certificate when configured when tls-mode is set to verify-certificate, or is set to dont-verify-certificate. Client needs a certificate only if Access Point is configured with tls-mode set to verify-certificate. In this case client needs a valid certificate that is signed by a CA known to the Access Point. This property only has effect when tls-mode is not set to no-certificates and eap-methods contains eap-tls. - `tls_mode` (String) This property has effect only when eap-methods contains eap-tls. `verify-certificate` - Require remote device to have valid certificate. Check that it is signed by known certificate authority. No additional identity verification is done. Certificate may include information about time period during which it is valid. If router has incorrect time and date, it may reject valid certificate because router's clock is outside that period. See also the Certificates configuration. `dont-verify-certificate` - Do not check certificate of the remote device. Access Point will not require client to provide certificate. `no-certificates` - Do not use certificates. TLS session is established using 2048 bit anonymous Diffie-Hellman key exchange. `verify-certificate-with-crl` - Same as verify-certificate but also checks if the certificate is valid by checking the Certificate Revocation List. - `unicast_ciphers` (String) Access Point advertises that it supports specified ciphers, multiple values can be selected. Client attempts connection only to Access Points that supports at least one of the specified ciphers. One of the ciphers will be used to encrypt unicast frames that are sent between Access Point and Station. - `wpa2_pre_shared_key` (String, Sensitive) `WPA2` pre-shared key mode requires all devices in a BSS to have common secret key. Value of this key can be an arbitrary text. Commonly referred to as the network password for WPA2 mode. property only has effect when wpa2-psk is added to authentication-types. - `wpa_pre_shared_key` (String, Sensitive) `WPA` pre-shared key mode requires all devices in a BSS to have common secret key. Value of this key can be an arbitrary text. Commonly referred to as the network password for WPA mode. property only has effect when wpa-psk is added to authentication-types. ### Read-Only - `default` (Boolean) It's the default item. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wireless/security-profiles get [print show-ids]] terraform import routeros_interface_wireless_security_profiles.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_wireless_security_profiles.test "name=xxx" ``` ================================================ FILE: docs/resources/ip_address.md ================================================ # routeros_ip_address (Resource) ## Example Usage ```terraform resource "routeros_ip_address" "address" { address = "10.0.0.1/24" interface = "bridge" network = "10.0.0.0" } ``` ## Schema ### Required - `address` (String) IP address. - `interface` (String) Name of the interface. ### Optional - `comment` (String) - `disabled` (Boolean) - `network` (String) IP address for the network. For point-to-point links it should be the address of the remote end. Starting from v5RC6 this parameter is configurable only for addresses with /32 netmask (point to point links) - `vrf` (String) The VRF table this resource operates on. ### Read-Only - `actual_interface` (String) Name of the actual interface the logical one is bound to. - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. - `invalid` (Boolean) - `slave` (Boolean) Whether address belongs to an interface which is a slave port to some other master interface ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/address get [print show-ids]] terraform import routeros_ip_address.address "*0" #Or you can import a resource using one of its attributes terraform import routeros_ip_address.address "name=xxx" ``` ================================================ FILE: docs/resources/ip_cloud.md ================================================ # routeros_ip_cloud (Resource) ## Example Usage ```terraform resource "routeros_ip_cloud" "test" { ddns_enabled = true update_time = false ddns_update_interval = "11m" } ``` ## Schema ### Optional - `back_to_home_vpn` (String) Enables or revokes and disables the Back to Home service. ddns-enabled has to be set to yes, for BTH to function. - `ddns_enabled` (String) If set to yes, then the device will send an encrypted message to the MikroTik's Cloud server. The server will then decrypt the message and verify that the sender is an authentic MikroTik device. If all is OK, then the MikroTik's Cloud server will create a DDNS record for this device and send a response to the device. Every minute the IP/Cloud service on the router will check if WAN IP address matches the one sent to MikroTik's Cloud server and will send encrypted update to cloud server if IP address changes. - `ddns_update_interval` (String) If set DDNS will attempt to connect IP Cloud servers at the set interval. If set to none it will continue to internally check IP address update and connect to IP Cloud servers as needed. Useful if IP address used is not on the router itself and thus, cannot be checked as a value internal to the router. - `update_time` (String) If set to yes then router clock will be set to time, provided by cloud server IF there is no NTP or SNTP client enabled. If set to no, then IP/Cloud service will never update the device's clock. If update-time is set to yes, Clock will be updated even when ddns-enabled is set to no. - `vpn_prefer_relay_code` (String) You can enter relay code that will be preferred for BTH connection, if not set, relay with smallest RTT will be chosen. ### Read-Only - `dns_name` (String) Shows DNS name assigned to the rdevice. Name consists of 12 character serial number appended by .sn.mynetname.net. This field is visible only after at least one ddns-request is successfully completed. - `id` (String) The ID of this resource. - `public_address` (String) Shows device's IPv4 address that was sent to cloud server. This field is visible only after at least one IP Cloud request was successfully completed. - `public_address_ipv6` (String) Shows device's IPv6 address that was sent to cloud server. This field is visible only after at least one IP Cloud request was successfully completed. - `status` (String) Contains text string that describes current dns-service state. The messages are self explanatory updating... updated Error: no Internet connection Error: request timed out Error: REJECTED. Contact MikroTik support Error: internal error - should not happen. One possible cause is if router runs out of memory. - `warning` (String) Shows a warning message if IP address sent by the device differs from the IP address in UDP packet header as visible by the MikroTik's Cloud server. Typically this happens if the device is behind NAT. Example: 'DDNS server received request from IP 123.123.123.123 but your local IP was 192.168.88.23; DDNS service might not work' ## Import Import is supported using the following syntax: ```shell terraform import routeros_ip_cloud.test . ``` ================================================ FILE: docs/resources/ip_cloud_advanced.md ================================================ # routeros_ip_cloud_advanced (Resource) ## Example Usage ```terraform resource "routeros_ip_cloud_advanced" "settings" { use_local_address = true } ``` ## Schema ### Optional - `use_local_address` (Boolean) An option whether to assign an internal router address to the dynamic DNS name. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_ip_cloud_advanced.settings . ``` ================================================ FILE: docs/resources/ip_dhcp_client.md ================================================ # routeros_ip_dhcp_client (Resource) ## Example Usage ```terraform resource "routeros_ip_dhcp_client" "client" { interface = "bridge" } ``` ## Schema ### Required - `interface` (String) Name of the interface. ### Optional - `add_default_route` (String) Whether to install default route in routing table received from DHCP server. - `allow_reconfigure` (Boolean) - `check_gateway` (String) Method on how to check gateway reachability. - `comment` (String) - `default_route_distance` (Number) Distance of default route. Applicable if add-default-route is set to yes. - `default_route_tables` (Set of String) Default route tables. - `dhcp_options` (String) Options that are sent to the DHCP server. - `disabled` (Boolean) - `dscp` (Number) Sets the DSCP (Differentiated Services Code Point) value for outgoing DHCP client packets. This value is part of the IP header and is used to indicate the desired Quality of Service (QoS) level for network traffic. - `script` (String) A script. - `use_broadcast` (String) Whether to set broadcast bit in DHCPDISCOVER and DHCPREQUEST messages. - `always` - broadcast bit is set always - `both` - broadcast bit is set only first 15 seconds - `never` - broadcast bit is not set - `use_peer_dns` (Boolean) Whether to accept the DNS settings advertised by DHCP Server (will override the settings put in the /ip dns submenu). - `use_peer_ntp` (Boolean) Whether to accept the NTP settings advertised by DHCP Server (will override the settings put in the /system ntp client submenu). - `use_reconfigure` (Boolean) Allow the server to send Reconfigure messages to clients, prompting them to renew or update their configuration without waiting for their lease to expire. - `vlan_priority` (Number) If the DHCP client is running on a VLAN interface (`/interface/vlan`), you can specify the Priority Code Point (PCP) value. PCP is a 3-bit field in the VLAN header used to mark the priority of packets within a VLAN, allowing traffic to be prioritized accordingly. This setting applies only to VLAN interfaces and affects the priority of outgoing DHCP client packets. ### Read-Only - `address` (String) IP address and netmask, which is assigned to DHCP Client from the Server. - `dhcp_server` (String) The IP address of the DHCP server. - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `expires_after` (String) A time when the lease expires (specified by the DHCP server). - `gateway` (String) The IP address of the gateway which is assigned by DHCP server. - `id` (String) The ID of this resource. - `invalid` (Boolean) - `primary_dns` (String) The IP address of the first DNS resolver, that was assigned by the DHCP server. - `primary_ntp` (String) The IP address of the primary NTP server, assigned by the DHCP server. - `secondary_dns` (String) The IP address of the second DNS resolver, assigned by the DHCP server. - `secondary_ntp` (String) The IP address of the secondary NTP server, assigned by the DHCP server. - `status` (String) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/dhcp-client get [print show-ids]] terraform import routeros_ip_dhcp_client.client "*0" #Or you can import a resource using one of its attributes terraform import routeros_ip_dhcp_client.client "name=xxx" ``` ================================================ FILE: docs/resources/ip_dhcp_client_option.md ================================================ # routeros_ip_dhcp_client_option (Resource) ## Example Usage ```terraform resource "routeros_ip_dhcp_client_option" "option" { name = "my-dhcp-option" code = 60 } ``` ## Schema ### Required - `code` (Number) The dhcp-client option code. - `name` (String) The name that will be used in dhcp-client. ### Optional - `raw_value` (String) raw_value is computed from value. - `value` (String) The dhcp-client option ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/dhcp-client/option get [print show-ids]] terraform import routeros_ip_dhcp_client_option.option "*0" #Or you can import a resource using one of its attributes terraform import routeros_ip_dhcp_client_option.option "name=xxx" ``` ================================================ FILE: docs/resources/ip_dhcp_relay.md ================================================ # routeros_ip_dhcp_relay (Resource) ## Example Usage ```terraform resource "routeros_ip_dhcp_relay" "relay" { name = "test relay" interface = "ether1" dhcp_server = "0.0.0.1" } ``` ## Schema ### Required - `dhcp_server` (String) List of DHCP servers' IP addresses which should the DHCP requests be forwarded to. - `interface` (String) Interface name the DHCP relay will be working on. - `name` (String) Descriptive name for the relay. ### Optional - `add_relay_info` (Boolean) Adds DHCP relay agent information if enabled according to RFC 3046. Agent Circuit ID Sub-option contains mac address of an interface, Agent Remote ID Sub-option contains MAC address of the client from which request was received. - `delay_threshold` (String) If secs field in DHCP packet is smaller than delay-threshold, then this packet is ignored. - `dhcp_server_vrf` (String) The VRF table this resource operates on. - `disabled` (Boolean) - `local_address` (String) The unique IP address of this DHCP relay needed for DHCP server to distinguish relays. If set to 0.0.0.0 - the IP address will be chosen automatically - `relay_info_remote_id` (String) Specified string will be used to construct Option 82 instead of client's MAC address. Option 82 consist of: interface from which packets was received + client mac address or relay-info-remote-id ### Read-Only - `id` (String) The ID of this resource. - `invalid` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/dhcp-relay get [print show-ids]] terraform import routeros_ip_dhcp_relay.relay "*0" #Or you can import a resource using one of its attributes terraform import routeros_ip_dhcp_relay.relay "name=xxx" ``` ================================================ FILE: docs/resources/ip_dhcp_server.md ================================================ # routeros_ip_dhcp_server (Resource) ## Example Usage ```terraform resource "routeros_ip_dhcp_server" "server" { address_pool = "my_address_pool" interface = "bridge" name = "bridge_dhcp" } ``` ## Schema ### Required - `interface` (String) Name of the interface. - `name` (String) Changing the name of this resource will force it to be recreated. > The links of other configuration properties to this resource may be lost! > Changing the name of the resource outside of a Terraform will result in a loss of control integrity for that resource! ### Optional - `add_arp` (Boolean) Whether to add dynamic ARP entry. - `address_lists` (Set of String) Address list to which address will be added if lease is bound. - `address_pool` (String) IP pool, from which to take IP addresses for the clients. If set to static-only, then only the clients that have a static lease (added in lease submenu) will be allowed. - `allow_dual_stack_queue` (Boolean) Creates a single simple queue entry for both IPv4 and IPv6 addresses, uses the MAC address and DUID for identification. Requires IPv6 DHCP Server to have this option enabled as well to work properly. - `always_broadcast` (Boolean) Always send replies as broadcasts even if destination IP is known. - `authoritative` (String) Option changes the way how a server responds to DHCP requests. - `bootp_lease_time` (String) Accepts two predefined options or time value: * forever - lease never expires * lease-time - use time from lease-time parameter - `bootp_support` (String) Support for BOOTP clients. - `client_mac_limit` (Number) Specifies whether to limit specific number of clients per single MAC address. - `comment` (String) - `conflict_detection` (Boolean) Allows to disable/enable conflict detection. If option is enabled, then whenever server tries to assign a lease it will send ICMP and ARP messages to detect whether such address in the network already exist. If any of above get reply address is considered already used. Conflict detection must be disabled when any kind of DHCP client limitation per port or per mac is used. - `delay_threshold` (String) If secs field in DHCP packet is smaller than delay-threshold, then this packet is ignored. If set to none - there is no threshold (all DHCP packets are processed). - `dhcp_option_set` (String) Use custom set of DHCP options defined in option sets menu. - `disabled` (Boolean) - `dynamic_lease_identifiers` (String) Dynamic lease identifier - `insert_queue_before` (String) Specify where to place dynamic simple queue entries for static DCHP leases with rate-limit parameter set. - `lease_script` (String) A script that will be executed after a lease is assigned or de-assigned. - `lease_time` (String) The time that a client may use the assigned address. The client will try to renew this address after half of this time and will request a new address after the time limit expires. - `parent_queue` (String) - `relay` (String) The IP address of the relay this DHCP server. - `src_address` (String) The address which the DHCP client must send requests to in order to renew an IP address lease. - `support_broadband_tr101` (Boolean) Support broadband TR101 - `use_framed_as_classless` (Boolean) Forward RADIUS Framed-Route as a DHCP Classless-Static-Route to DHCP-client. - `use_radius` (String) Whether to use RADIUS server. - `use_reconfigure` (Boolean) Allow the server to send Reconfigure (forcerenew) messages to clients, prompting them to renew configuration without waiting for their lease to expire. ### Read-Only - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. - `invalid` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/dhcp-server get [print show-ids]] terraform import routeros_ip_dhcp_server.server "*1" ``` ================================================ FILE: docs/resources/ip_dhcp_server_config.md ================================================ # routeros_ip_dhcp_server_config (Resource) ## Example Usage ```terraform resource "routeros_ip_dhcp_server_config" "settings" { accounting = true interim_update = "1m" radius_password = "same-as-user" store_leases_disk = "10m" } ``` ## Schema ### Optional - `accounting` (Boolean) An option that enables accounting for DHCP leases. - `interim_update` (String) An option determining whether the DHCP server sends periodic updates to the accounting server during a lease. - `radius_password` (String) An option to set the password parameter for the RADIUS server. This option is available in RouterOS starting from version 7.0. - `store_leases_disk` (String) An option of how often the DHCP leases will be stored on disk. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_ip_dhcp_server_config.settings . ``` ================================================ FILE: docs/resources/ip_dhcp_server_lease.md ================================================ # routeros_ip_dhcp_server_lease (Resource) Creates a DHCP lease on the mikrotik device. ## Example Usage ```terraform resource "routeros_ip_dhcp_server_lease" "dhcp_lease" { address = "10.0.0.2" mac_address = "AA:BB:CC:DD:11:22" } ``` ## Schema ### Required - `address` (String) The IP address of the DHCP lease to be created. - `mac_address` (String) The MAC addreess of the DHCP lease to be created. ### Optional - `address_lists` (String) Address list to which address will be added if lease is bound. - `allow_dual_stack_queue` (Boolean) Creates a single simple queue entry for both IPv4 and IPv6 addresses, uses the MAC address and DUID for identification. - `always_broadcast` (Boolean) Send all replies as broadcasts. - `block_access` (Boolean) Whether to block access for this DHCP client (true|false). - `client_id` (String) If specified, must match DHCP 'client identifier' option of the request. - `comment` (String) - `dhcp_option` (String) Add additional DHCP options. - `dhcp_option_set` (String) Add additional set of DHCP options. - `disabled` (Boolean) - `insert_queue_before` (String) Specify where to place dynamic simple queue entries for static DCHP leases with rate-limit parameter set. - `lease_time` (String) Time that the client may use the address. If set to 0s lease will never expire. - `rate_limit` (String) Adds a dynamic simple queue to limit IP's bandwidth to a specified rate. Requires the lease to be static. - `server` (String) Server name which serves this client. - `use_src_mac` (Boolean) When this option is set server uses source MAC address instead of received CHADDR to assign address. ### Read-Only - `active_address` (String) The IP address of the machine currently holding the DHCP lease. - `active_client_id` (String) Actual client-id of the client. - `active_hostname` (String) The hostname of the machine currently holding the DHCP lease. - `active_mac_address` (String) The MAC address of of the machine currently holding the DHCP lease. - `active_server` (String) Actual dhcp server, which serves this client. - `agent_circuit_id` (String) Circuit ID of DHCP relay agent. If each character should be valid ASCII text symbol or else this value is displayed as hex dump. - `agent_remote_id` (String) Remote ID, set by DHCP relay agent. - `blocked` (Boolean) Whether the lease is blocked. - `class_id` (String) Class ID of the client. This option is available in RouterOS starting from version 7.16. - `dynamic` (Boolean) Whether the dhcp lease is static or dynamic. Dynamic leases are not guaranteed to continue to be assigned to that specific device. - `expires_after` (String) Time until lease expires. - `host_name` (String) The hostname of the device - `id` (String) The ID of this resource. - `last_seen` (String) - `radius` (String) Shows if this dynamic lease is authenticated by RADIUS or not. - `src_mac_address` (String) Source MAC address. - `status` (String) Lease status. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/dhcp-server/lease get [print show-ids]] terraform import routeros_ip_dhcp_server_lease.dhcp_lease "*0" #Or you can import a resource using one of its attributes terraform import routeros_ip_dhcp_server_lease.dhcp_lease "name=xxx" ``` ================================================ FILE: docs/resources/ip_dhcp_server_network.md ================================================ # routeros_ip_dhcp_server_network (Resource) ## Example Usage ```terraform resource "routeros_ip_dhcp_server_network" "dhcp_server_network" { address = "10.0.0.0/24" gateway = "10.0.0.1" dns_server = ["1.1.1.1"] } ``` ## Schema ### Required - `address` (String) The network DHCP server(s) will lease addresses from. ### Optional - `boot_file_name` (String) Boot filename. - `caps_manager` (List of String) A list of IP addresses for one or more CAPsMAN system managers. DHCP Option 138 (capwap) will be used. - `comment` (String) - `dhcp_option` (List of String) Add additional DHCP options from the option list. - `dhcp_option_set` (String) Add an additional set of DHCP options. - `dns_none` (Boolean) If set, then DHCP Server will not pass dynamic DNS servers configured on the router to the DHCP clients if no DNS Server in DNS-server is set. - `dns_server` (List of String) The DHCP client will use these as the default DNS servers. Two DNS servers can be specified to be used by the DHCP client as primary and secondary DNS servers. - `domain` (String) The DHCP client will use this as the 'DNS domain' setting for the network adapter. - `gateway` (String) The default gateway to be used by DHCP Client. - `netmask` (Number) The actual network mask is to be used by the DHCP client. If set to '0' - netmask from network address will be used. - `next_server` (String) The IP address of the next server to use in bootstrap. - `ntp_none` (Boolean) If set, then DHCP Server will not pass NTP servers configured on the router to the DHCP clients. - `ntp_server` (List of String) The DHCP client will use these as the default NTP servers. Two NTP servers can be specified to be used by the DHCP client as primary and secondary NTP servers - `wins_server` (List of String) The Windows DHCP client will use these as the default WINS servers. Two WINS servers can be specified to be used by the DHCP client as primary and secondary WINS servers ### Read-Only - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/dhcp-server/network get [print show-ids]] terraform import routeros_ip_dhcp_server_network.dhcp_server_network "*0" #Or you can import a resource using one of its attributes terraform import routeros_ip_dhcp_server_network.dhcp_server_network "name=xxx" ``` ================================================ FILE: docs/resources/ip_dhcp_server_option.md ================================================ # routeros_ip_dhcp_server_option (Resource) Creates a DHCP lease on the mikrotik device. ## Example Usage ```terraform resource "routeros_ip_dhcp_server_option" "jumbo_frame_opt" { code = 77 name = "jumbo-mtu-opt" value = "0x2336" } resource "routeros_ip_dhcp_server_option" "tftp_option" { code = 66 name = "tftpserver-66" value = "s'10.10.10.22'" } ``` ## Schema ### Required - `code` (Number) The number of the DHCP option - `name` (String) The name of the DHCP option - `value` (String) The value with formatting using Mikrotik settings https://wiki.mikrotik.com/wiki/Manual:IP/DHCP_Server#DHCP_Options ### Optional - `comment` (String) - `force` (Boolean) Force the DHCP option from the server-side even if the DHCP-client does not request such parameter. ### Read-Only - `id` (String) The ID of this resource. - `raw_value` (String) The computed value of the option as an hex value ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/dhcp-server/option/get [print show-ids]] terraform import routeros_ip_dhcp_server_option.tftp_option "*1" #Or you can import a resource using one of its attributes terraform import routeros_ip_dhcp_server_option.tftp_option "name=xxx" ``` ================================================ FILE: docs/resources/ip_dhcp_server_option_matcher.md ================================================ # routeros_ip_dhcp_server_option_matcher (Resource) ## Example Usage ```terraform resource "routeros_ip_dhcp_server_option_matcher" "dhcp1_ip_by_vendor_class" { name = "dhcp1_ip_by_vendor_class" server = "dhcp1" address_pool = "pool1" code = 60 # Vendor Class Identifier value = "android-dhcp-11" matching_type = "exact" } ``` ## Schema ### Required - `code` (Number) DHCP option code. All codes are available at http://www.iana.org/assignments/bootp-dhcp-parameters - `name` (String) Changing the name of this resource will force it to be recreated. > The links of other configuration properties to this resource may be lost! > Changing the name of the resource outside of a Terraform will result in a loss of control integrity for that resource! ### Optional - `address_pool` (String) IP pool, from which to take IP addresses for the clients. If set to static-only, then only the clients that have a static lease (added in lease submenu) will be allowed. - `comment` (String) - `disabled` (Boolean) - `matching_type` (String) Matching method: - exact: option should match exactly to value - substring: value can match anywhere in the option string; at the start, middle, or end. - `option_set` (String) A custom set of DHCP options defined in the Option Sets menu. - `server` (String) Server name which serves option matcher. - `value` (String) A value that will be searched for in option. Available data types for value are: - string - HEX ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/dhcp-server/matcher/get [print show-ids]] terraform import routeros_ip_dhcp_server_option_matcher.test "*1" ``` ================================================ FILE: docs/resources/ip_dhcp_server_option_set.md ================================================ # routeros_ip_dhcp_server_option_set (Resource) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_ip_dhcp_server_option_sets](ip_dhcp_server_option_sets.md) ================================================ FILE: docs/resources/ip_dhcp_server_option_sets.md ================================================ # routeros_ip_dhcp_server_option_sets (Resource) Creates a DHCP lease on the mikrotik device. ## Example Usage ```terraform resource "routeros_ip_dhcp_server_option" "jumbo_frame_opt" { code = 77 name = "jumbo-mtu-opt" value = "0x2336" } resource "routeros_ip_dhcp_server_option" "tftp_option" { code = 66 name = "tftpserver-66" value = "s'10.10.10.22'" } resource "routeros_ip_dhcp_server_option_sets" "lan_option_set" { name = "lan-option-set" options = join(",", [routeros_ip_dhcp_server_option.jumbo_frame_opt.name, routeros_ip_dhcp_server_option.tftp_option.name]) } ``` ## Schema ### Required - `name` (String) The name of the DHCP option - `options` (String) The comma sepparated list of options ### Optional - `comment` (String) ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/dhcp-server/option/sets/get [print show-ids]] terraform import routeros_ip_dhcp_server_option_sets.lan_option_set "*1" ``` ================================================ FILE: docs/resources/ip_dns.md ================================================ # routeros_ip_dns (Resource) A MikroTik router with DNS feature enabled can be set as a DNS server for any DNS-compliant client. ## Example Usage ```terraform resource "routeros_ip_dns" "dns-server" { allow_remote_requests = true servers = [ "2606:4700:4700::1111,1.1.1.1", "2606:4700:4700::1001,1.0.0.1", ] } ``` ## Schema ### Optional - `address_list_extra_time` (String) - `allow_remote_requests` (Boolean) Specifies whether to allow network requests. - `cache_max_ttl` (String) Maximum time-to-live for cache records. In other words, cache records will expire unconditionally after cache-max-ttl time. Shorter TTL received from DNS servers are respected. *Default: 1w* - `cache_size` (Number) Specifies the size of DNS cache in KiB (64..4294967295). *Default: 2048* - `doh_max_concurrent_queries` (Number) Specifies how many DoH concurrent queries are allowed. - `doh_max_server_connections` (Number) Specifies how many concurrent connections to the DoH server are allowed. - `doh_timeout` (String) Specifies how long to wait for query response from the DoH server. - `max_concurrent_queries` (Number) Specifies how much concurrent queries are allowed. *Default: 100* - `max_concurrent_tcp_sessions` (Number) Specifies how much concurrent TCP sessions are allowed. *Default: 20* - `max_udp_packet_size` (Number) Maximum size of allowed UDP packet. *Default: 4096* - `mdns_repeat_ifaces` (Set of String) An option to enable mDNS repeater on specified interfaces. This option is available in RouterOS starting from version 7.16. - `query_server_timeout` (String) Specifies how long to wait for query response from one server. Time can be specified in milliseconds. *Default: 2s* - `query_total_timeout` (String) Specifies how long to wait for query response in total. Note that this setting must be configured taking into account query_server_timeout and number of used DNS server. Time can be specified in milliseconds. *Default: 10s* - `servers` (List of String) List of DNS server IPv4/IPv6 addresses. - `use_doh_server` (String) DNS over HTTPS (DoH) server URL. > Mikrotik strongly suggest not use third-party download links for certificate fetching. Use the Certificate Authority's own website. > RouterOS prioritize DoH over DNS server if both are configured on the device. - `verify_doh_cert` (Boolean) DoH certificate verification. [See docs](https://wiki.mikrotik.com/wiki/Manual:IP/DNS#DNS_over_HTTPS). - `vrf` (String) The VRF table this resource operates on. ### Read-Only - `cache_used` (Number) Shows the currently used cache size in KiB. - `dynamic_servers` (String) List of dynamically added DNS server from different services, for example, DHCP. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The DNS Settings can not be imported. #Terraform will ignore the current settings and will overwrite the current settings with the settings defined in Terraform. ``` ================================================ FILE: docs/resources/ip_dns_adlist.md ================================================ # routeros_ip_dns_adlist (Resource) ## Example Usage ```terraform resource "routeros_ip_dns_adlist" "test" { url = "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts" ssl_verify = false } ``` ## Schema ### Optional - `disabled` (Boolean) - `file` (String) Used to specify a local file path from which to read adlist data. - `ssl_verify` (Boolean) Specifies whether to validate the server's SSL certificate when connecting to an online resource. Will use the `/certificate` list to verify server validity. - `url` (String) Used to specify the URL of an adlist. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/dns/adlist get [print show-ids]] terraform import routeros_ip_dns_adlist.test "*0" #Or you can import a resource using one of its attributes terraform import routeros_ip_dns_adlist.test "name=xxx" ``` ================================================ FILE: docs/resources/ip_dns_forwarders.md ================================================ # routeros_ip_dns_forwarders (Resource) ## Example Usage ```terraform resource "routeros_ip_dns_forwarders" "test" { disabled = true dns_servers = ["1.1.1.1"] doh_servers = ["2.2.2.2"] name = "test" verify_doh_cert = "false" } ``` ## Schema ### Required - `name` (String) Forwarder name. ### Optional - `comment` (String) - `disabled` (Boolean) - `dns_servers` (Set of String) An IP address or DNS name of a domain name server. Can contain multiple records. - `doh_servers` (Set of String) A URL of DoH server. Can contain multiple records. - `verify_doh_cert` (Boolean) Specifies whether to validate the DoH server, when one is being used. Will use the `/certificate` list in order to verify server validity. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/dns/forwarders get [print show-ids]] terraform import routeros_ip_dns_forwarders.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_dns_forwarders.test "name=xxx" ``` ================================================ FILE: docs/resources/ip_dns_record.md ================================================ # routeros_ip_dns_record (Resource) Creates a DNS record on the MikroTik device. ## Example Usage ```terraform resource "routeros_ip_dns_record" "name_record" { name = "router.lan" address = "192.168.88.1" type = "A" } resource "routeros_ip_dns_record" "regexp_record" { regexp = ".*pool.ntp.org" address = "192.168.88.1" type = "A" } resource "routeros_dns_record" "aaaa_record" { name = "ipv6.lan" address = "ff00::1" type = "AAAA" } resource "routeros_dns_record" "cname_record" { name = "cname.lan" cname = "ipv4.lan" type = "CNAME" } resource "routeros_dns_record" "fwd_record" { name = "fwd.lan" forward_to = "127.0.0.1" type = "FWD" } resource "routeros_dns_record" "mx_record" { name = "mx.lan" mx_exchange = "127.0.0.1" mx_preference = 10 type = "MX" } resource "routeros_dns_record" "ns_record" { name = "ns.lan" ns = "127.0.0.1" type = "NS" } resource "routeros_dns_record" "nxdomain_record" { name = "nxdomain.lan" type = "NXDOMAIN" } resource "routeros_dns_record" "srv_record" { name = "srv.lan" srv_port = 8080 srv_priority = 10 srv_target = "127.0.0.1" srv_weight = 100 type = "SRV" } resource "routeros_dns_record" "txt_record" { name = "_acme-challenge.yourwebsite.com" text = "dW6MrI3nBy3eJgYWH3QAg1Cwk_TvjFESOuKo+mp6nm1" type = "TXT" } ``` ## Schema ### Required - `type` (String) Type of the DNS record. Available values are: A, AAAA, CNAME, FWD, MX, NS, NXDOMAIN, SRV, TXT ### Optional - `address` (String) The A record to be returend from the DNS hostname. - `address_list` (String) Name of the Firewall address list to which address must be dynamically added when some request matches the entry. - `cname` (String) Alias name for a domain name. - `comment` (String) - `disabled` (Boolean) - `forward_to` (String) The IP address of a domain name server to which a particular DNS request must be forwarded. - `match_subdomain` (Boolean) Whether the record will match requests for subdomains. - `mx_exchange` (String) The domain name of the MX server. - `mx_preference` (Number) Preference of the particular MX record. - `name` (String) The name of the DNS hostname to be created. - `ns` (String) Name of the authoritative domain name server for the particular record. - `regexp` (String) DNS regexp. Regexp entries are case sensitive, but since DNS requests are not case sensitive, RouterOS converts DNS names to lowercase, you should write regex only with lowercase letters. - `srv_port` (Number) The TCP or UDP port on which the service is to be found. - `srv_priority` (Number) Priority of the particular SRV record. - `srv_target` (String) The canonical hostname of the machine providing the service ends in a dot. - `srv_weight` (String) Weight of the particular SRC record. - `text` (String) Textual information about the domain name. - `ttl` (String) The ttl of the DNS record. ### Read-Only - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/dns/static get [print show-ids]] terraform import routeros_ip_dns_record.name_record "*0" #Or you can import a resource using one of its attributes terraform import routeros_ip_dns_record.name_record "name=xxx" ``` ================================================ FILE: docs/resources/ip_firewall_addr_list.md ================================================ # routeros_ip_firewall_addr_list (Resource) ## Example Usage ```terraform resource "routeros_ip_firewall_addr_list" "example_list" { address = "1.1.1.1" list = "Example List" } ``` ## Schema ### Required - `address` (String) A single IP address or range of IPs to add to address list or DNS name. You can input for example, '192.168.0.0-192.168.1.255' and it will auto modify the typed entry to 192.168.0.0/23 on saving. - `list` (String) Name for the address list of the added IP address. ### Optional - `comment` (String) - `disabled` (Boolean) - `timeout` (String) Time after address will be removed from address list. If timeout is not specified, the address will be stored into the address list permanently. > Please plan your work logic based on the fact that after the timeout > the resource has been destroyed outside of a Terraform. ### Read-Only - `creation_time` (String) Rule creation time - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/firewall/address-list get [print show-ids]] terraform import routeros_ip_firewall_addr_list.example_list "*0" #Or you can import a resource using one of its attributes terraform import routeros_ip_firewall_addr_list.example_list "name=xxx" ``` ================================================ FILE: docs/resources/ip_firewall_connection_tracking.md ================================================ # routeros_ip_firewall_connection_tracking (Resource) ## Example Usage ```terraform resource "routeros_ip_firewall_connection_tracking" "data" { enabled = "yes" generic_timeout = "3m" icmp_timeout = "3m" loose_tcp_tracking = "false" tcp_close_timeout = "3m" tcp_close_wait_timeout = "3m" tcp_established_timeout = "3m" tcp_fin_wait_timeout = "3m" tcp_last_ack_timeout = "3m" tcp_max_retrans_timeout = "3m" tcp_syn_received_timeout = "3m" tcp_syn_sent_timeout = "3m" tcp_time_wait_timeout = "3m" tcp_unacked_timeout = "3m" udp_stream_timeout = "3m" udp_timeout = "3m" } ``` ## Schema ### Optional - `enabled` (String) Allows to disable or enable connection tracking. Disabling connection tracking will cause several firewall features to stop working. See the list of affected features. Starting from v6.0rc2 default value is auto. This means that connection tracing is disabled until at least one firewall rule is added. - `generic_timeout` (String) Timeout for all other connection entries - `icmp_timeout` (String) ICMP connection timeout - `liberal_tcp_tracking` (Boolean) Enables or disables liberal TCP connection tracking by toggling the kernel parameter nf_conntrack_tcp_be_liberal. When set to `yes`, the system mark only out of window RST segments as INVALID. `Enabling this setting may allow malformed packets that would otherwise be considered invalid by the firewall's connection-state matcher. This can increase exposure to certain evasion techniques. This property should be enabled only when troubleshooting or working around known issues.` - `loose_tcp_tracking` (String) Disable picking up already established connections - `tcp_close_timeout` (String) No documentation - `tcp_close_wait_timeout` (String) No documentation - `tcp_established_timeout` (String) Time when established TCP connection times out. - `tcp_fin_wait_timeout` (String) No documentation - `tcp_last_ack_timeout` (String) No documentation - `tcp_max_retrans_timeout` (String) No documentation - `tcp_syn_received_timeout` (String) TCP SYN timeout. - `tcp_syn_sent_timeout` (String) TCP SYN timeout. - `tcp_time_wait_timeout` (String) No documentation - `tcp_unacked_timeout` (String) No documentation - `udp_stream_timeout` (String) Specifies the timeout of UDP connections that has seen packets in both directions - `udp_timeout` (String) Specifies the timeout for UDP connections that have seen packets in one direction ### Read-Only - `active_ipv4` (Boolean) documentation is missing - `active_ipv6` (Boolean) documentation is missing - `id` (String) The ID of this resource. - `max_entries` (String) Max amount of entries that the connection tracking table can hold. This value depends on the installed amount of RAM. Note that the system does not create a maximum_size connection tracking table when it starts, it may increase if the situation demands it and the system still has free ram, but size will not exceed 1048576 ================================================ FILE: docs/resources/ip_firewall_filter.md ================================================ # routeros_ip_firewall_filter (Resource) ## Example Usage ```terraform resource "routeros_ip_firewall_filter" "rule" { action = "accept" chain = "forward" src_address = "10.0.0.1" dst_address = "10.0.1.1" dst_port = "443" protocol = "tcp" } ``` ## Schema ### Required - `action` (String) Action to take if a packet is matched by the rule - `chain` (String) Specifies to which chain rule will be added. If the input does not match the name of an already defined chain, a new chain will be created. ### Optional - `address_list` (String) Name of the address list used in 'add-dst-to-address-list' and 'add-src-to-address-list' actions. - `address_list_timeout` (String) Time interval after which the address will be removed from the address list specified by address-list parameter. Used in conjunction with add-dst-to-address-list or add-src-to-address-list actions. - `comment` (String) - `connection_bytes` (String) Matches packets only if a given amount of bytes has been transfered through the particular connection. - `connection_limit` (String) Matches connections per address or address block after given value is reached. Should be used together with connection-state=new and/or with tcp-flags=syn because matcher is very resource intensive. - `connection_mark` (String) Matches packets marked via mangle facility with particular connection mark. If no-mark is set, rule will match any unmarked connection. - `connection_nat_state` (String) Can match connections that are srcnatted, dstnatted or both. - `connection_rate` (String) Connection Rate is a firewall matcher that allow to capture traffic based on present speed of the connection (0..4294967295). - `connection_state` (String) Interprets the connection tracking analysis data for a particular packet. - `connection_type` (String) Matches packets from related connections based on information from their connection tracking helpers. - `content` (String) Match packets that contain specified text. - `disabled` (Boolean) - `dscp` (Number) Matches DSCP IP header field. - `dst_address` (String) Matches packets which destination is equal to specified IP or falls into specified IP range. - `dst_address_list` (String) Matches destination address of a packet against user-defined address list. - `dst_address_type` (String) Matches destination address type. - `dst_limit` (String) Matches packets until a given rate is exceeded. - `dst_port` (String) List of destination port numbers or port number ranges. - `fragment` (Boolean) Matches fragmented packets. First (starting) fragment does not count. If connection tracking is enabled there will be no fragments as system automatically assembles every packet - `hotspot` (String) Matches packets received from HotSpot clients against various HotSpot matchers. - `hw_offload` (Boolean) Connection offloading for Fasttrack. - `icmp_options` (String) Matches ICMP type: code fields. - `in_bridge_port` (String) Actual interface the packet has entered the router if the incoming interface is a bridge. Works only if use-ip-firewall is enabled in bridge settings. - `in_bridge_port_list` (String) Set of interfaces defined in interface list. Works the same as in-bridge-port. - `in_interface` (String) Interface the packet has entered the router. - `in_interface_list` (String) Set of interfaces defined in interface list. Works the same as in-interface. - `ingress_priority` (Number) Matches the priority of an ingress packet. Priority may be derived from VLAN, WMM, DSCP, or MPLS EXP bit. - `ipsec_policy` (String) Matches the policy used by IPsec. Value is written in the following format: direction, policy. - `ipv4_options` (String) Matches IPv4 header options. - `jump_target` (String) Name of the target chain to jump to. Applicable only if action=jump. - `layer7_protocol` (String) Layer7 filter name. - `limit` (String) Matches packets up to a limited rate (packet rate or bit rate). A rule using this matcher will match until this limit is reached. Parameters are written in the following format: rate[/time],burst:mode. - `log` (Boolean) Add a message to the system log. - `log_prefix` (String) Adds specified text at the beginning of every log message. Applicable if action=log or log=yes configured. - `nth` (String) Matches every nth packet: nth=2,1 rule will match every first packet of 2, hence, 50% of all the traffic that is matched by the rule - `out_bridge_port` (String) Actual interface the packet is leaving the router if the outgoing interface is a bridge. Works only if use-ip-firewall is enabled in bridge settings. - `out_bridge_port_list` (String) Set of interfaces defined in interface list. Works the same as out-bridge-port. - `out_interface` (String) Interface the packet is leaving the router. - `out_interface_list` (String) Set of interfaces defined in interface list. Works the same as out-interface. - `packet_mark` (String) Matches packets marked via mangle facility with particular packet mark. If no-mark is set, the rule will match any unmarked packet. - `packet_size` (String) Matches packets of specified size or size range in bytes. - `per_connection_classifier` (String) PCC matcher allows dividing traffic into equal streams with the ability to keep packets with a specific set of options in one particular stream. - `place_before` (String) Before which position the rule will be inserted. > Please check the effect of this option, as it does not work as you think! > Best way to use in conjunction with a data source. See [example](../data-sources/ip_firewall.md#example-usage). - `port` (String) Matches if any (source or destination) port matches the specified list of ports or port ranges. Applicable only if protocol is TCP or UDP - `priority` (Number) Matches the packet's priority after a new priority has been set. Priority may be derived from VLAN, WMM, DSCP, MPLS EXP bit, or from the priority that has been set using the set-priority action. - `protocol` (String) Matches particular IP protocol specified by protocol name or number. - `psd` (String) Attempts to detect TCP and UDP scans. Parameters are in the following format WeightThreshold, DelayThreshold, LowPortWeight, HighPortWeight. - `random` (Number) Matches packets randomly with a given probability. - `reject_with` (String) Specifies ICMP error to be sent back if the packet is rejected. Applicable if action=reject. - `routing_mark` (String) Matches packets marked by mangle facility with particular routing mark. - `routing_table` (String) Matches packets which destination address is resolved in specific a routing table. - `src_address` (String) Matches packets which source is equal to specified IP or falls into a specified IP range. - `src_address_list` (String) Matches source address of a packet against user-defined address list. - `src_address_type` (String) Matches source address type. - `src_mac_address` (String) Matches source MAC address of the packet. - `src_port` (String) List of source ports and ranges of source ports. Applicable only if a protocol is TCP or UDP. - `tcp_flags` (String) Matches specified TCP flags. - `tcp_mss` (String) Matches TCP MSS value of an IP packet. - `time` (String) Allows to create a filter based on the packets' arrival time and date or, for locally generated packets, departure time and date. - `tls_host` (String) Allows matching HTTPS traffic based on TLS SNI hostname. - `ttl` (String) Matches packets TTL value. ### Read-Only - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. - `invalid` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/firewall/filter get [print show-ids]] terraform import routeros_ip_firewall_filter.rule "*0" #Or you can import a resource using one of its attributes terraform import routeros_ip_firewall_filter.rule "name=xxx" ``` ================================================ FILE: docs/resources/ip_firewall_layer7_protocol.md ================================================ # routeros_ip_firewall_layer7_protocol (Resource) ## Example Usage ```terraform resource "routeros_ip_firewall_layer7_protocol" "test" { name = "rdp" regexp = "rdpdr.*cliprdr.*rdpsnd" } ``` ## Schema ### Optional - `name` (String) Descriptive name of l7 pattern used by configuration in firewall rules. - `regexp` (String) POSIX compliant regular expression is used to match a pattern. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/firewall/layer7-protocol get [print show-ids]] terraform import routeros_ip_firewall_layer7_protocol.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_firewall_layer7_protocol.test "name=xxx" ``` ================================================ FILE: docs/resources/ip_firewall_mangle.md ================================================ # routeros_ip_firewall_mangle (Resource) ## Example Usage ```terraform resource "routeros_ip_firewall_mangle" "rule" { action = "change-mss" chain = "forward" out_interface = "pppoe-out" protocol = "tcp" tcp_flags = "syn" new_mss = "1130" tcp_mss = "1301-65535" } ``` ## Schema ### Required - `action` (String) Action to take if a packet is matched by the rule. - `chain` (String) Specifies to which chain rule will be added. If the input does not match the name of an already defined chain, a new chain will be created. ### Optional - `address_list` (String) Name of the address list to be used. Applicable if action is add-dst-to-address-list or add-src-to-address-list. - `address_list_timeout` (String) Time interval after which the address will be removed from the address list specified by address-list parameter. Used in conjunction with add-dst-to-address-list or add-src-to-address-list actions. - `comment` (String) - `connection_bytes` (String) Matches packets only if a given amount of bytes has been transfered through the particular connection. - `connection_limit` (String) Matches connections per address or address block after given value is reached. Should be used together with connection-state=new and/or with tcp-flags=syn because matcher is very resource intensive. - `connection_mark` (String) Matches packets marked via mangle facility with particular connection mark. If no-mark is set, rule will match any unmarked connection. - `connection_nat_state` (String) Can match connections that are srcnatted, dstnatted or both. - `connection_rate` (String) Connection Rate is a firewall matcher that allow to capture traffic based on present speed of the connection (0..4294967295). - `connection_state` (String) Interprets the connection tracking analysis data for a particular packet. - `connection_type` (String) Matches packets from related connections based on information from their connection tracking helpers. - `content` (String) Match packets that contain specified text. - `disabled` (Boolean) - `dscp` (Number) Matches DSCP IP header field. - `dst_address` (String) Matches packets which destination is equal to specified IP or falls into specified IP range. - `dst_address_list` (String) Matches destination address of a packet against user-defined address list. - `dst_address_type` (String) Matches destination address type. - `dst_limit` (String) Matches packets until a given rate is exceeded. - `dst_port` (String) List of destination port numbers or port number ranges. - `fragment` (Boolean) Matches fragmented packets. First (starting) fragment does not count. If connection tracking is enabled there will be no fragments as system automatically assembles every packet - `hotspot` (String) Matches packets received from HotSpot clients against various HotSpot matchers. - `icmp_options` (String) Matches ICMP type: code fields. - `in_bridge_port` (String) Actual interface the packet has entered the router if the incoming interface is a bridge. Works only if use-ip-firewall is enabled in bridge settings. - `in_bridge_port_list` (String) Set of interfaces defined in interface list. Works the same as in-bridge-port. - `in_interface` (String) Interface the packet has entered the router. - `in_interface_list` (String) Set of interfaces defined in interface list. Works the same as in-interface. - `ingress_priority` (Number) Matches the priority of an ingress packet. Priority may be derived from VLAN, WMM, DSCP, or MPLS EXP bit. - `ipsec_policy` (String) Matches the policy used by IPsec. Value is written in the following format: direction, policy. - `ipv4_options` (String) Matches IPv4 header options. - `jump_target` (String) Name of the target chain to jump to. Applicable only if action=jump. - `layer7_protocol` (String) Layer7 filter name. - `limit` (String) Matches packets up to a limited rate (packet rate or bit rate). A rule using this matcher will match until this limit is reached. Parameters are written in the following format: rate[/time],burst:mode. - `log` (Boolean) Add a message to the system log. - `log_prefix` (String) Adds specified text at the beginning of every log message. Applicable if action=log or log=yes configured. - `new_connection_mark` (String) Sets a new connection-mark value. - `new_dscp` (Number) Sets a new DSCP value for a packet. - `new_mss` (String) Sets a new MSS for a packet. * clamp-to-pmtu feature sets (DF) bit in the IP header to dynamically discover the PMTU of a path. * Host sends all datagrams on that path with the DF bit set until receives ICMP. * Destination Unreachable messages with a code meaning `fragmentation needed and DF set`. * Upon receipt of such a message, the source host reduces its assumed PMTU for the path. - `new_packet_mark` (String) Sets a new packet-mark value. - `new_priority` (String) Sets a new priority for a packet. This can be the VLAN, WMM, DSCP or MPLS EXP priority. This property can also be used to set an internal priority. - `new_routing_mark` (String) Sets a new routing-mark value. - `new_ttl` (String) Sets a new TTL for a packet. - `nth` (String) Matches every nth packet: nth=2,1 rule will match every first packet of 2, hence, 50% of all the traffic that is matched by the rule - `out_bridge_port` (String) Actual interface the packet is leaving the router if the outgoing interface is a bridge. Works only if use-ip-firewall is enabled in bridge settings. - `out_bridge_port_list` (String) Set of interfaces defined in interface list. Works the same as out-bridge-port. - `out_interface` (String) Interface the packet is leaving the router. - `out_interface_list` (String) Set of interfaces defined in interface list. Works the same as out-interface. - `packet_mark` (String) Matches packets marked via mangle facility with particular packet mark. If no-mark is set, the rule will match any unmarked packet. - `packet_size` (String) Matches packets of specified size or size range in bytes. - `passthrough` (Boolean) Whether to let the packet to pass further (like action passthrough) into the firewall or not (property only valid some actions). - `per_connection_classifier` (String) PCC matcher allows dividing traffic into equal streams with the ability to keep packets with a specific set of options in one particular stream. - `place_before` (String) Before which position the rule will be inserted. > Please check the effect of this option, as it does not work as you think! > Best way to use in conjunction with a data source. See [example](../data-sources/ip_firewall.md#example-usage). - `port` (String) Matches if any (source or destination) port matches the specified list of ports or port ranges. Applicable only if protocol is TCP or UDP - `protocol` (String) Matches particular IP protocol specified by protocol name or number. - `psd` (String) Attempts to detect TCP and UDP scans. Parameters are in the following format WeightThreshold, DelayThreshold, LowPortWeight, HighPortWeight. - `random` (Number) Matches packets randomly with a given probability. - `route_dst` (String) Matches packets with a specific gateway. - `routing_mark` (String) Matches packets marked by mangle facility with particular routing mark. - `src_address` (String) Matches packets which source is equal to specified IP or falls into a specified IP range. - `src_address_list` (String) Matches source address of a packet against user-defined address list. - `src_address_type` (String) Matches source address type. - `src_mac_address` (String) Matches source MAC address of the packet. - `src_port` (String) List of source ports and ranges of source ports. Applicable only if a protocol is TCP or UDP. - `tcp_flags` (String) Matches specified TCP flags. - `tcp_mss` (String) Matches TCP MSS value of an IP packet. - `time` (String) Allows to create a filter based on the packets' arrival time and date or, for locally generated packets, departure time and date. - `tls_host` (String) Allows matching HTTPS traffic based on TLS SNI hostname. - `ttl` (String) Matches packets TTL value. ### Read-Only - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. - `invalid` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/firewall/mangle get [print show-ids]] terraform import routeros_ip_firewall_mangle.rule "*0" #Or you can import a resource using one of its attributes terraform import routeros_ip_firewall_mangle.rule "name=xxx" ``` ================================================ FILE: docs/resources/ip_firewall_nat.md ================================================ # routeros_ip_firewall_nat (Resource) ## Example Usage ```terraform resource "routeros_ip_firewall_nat" "rule" { action = "masquerade" chain = "srcnat" out_interface = "ether16" } ``` ## Schema ### Required - `action` (String) Action to take if a packet is matched by the rule - `chain` (String) Specifies to which chain rule will be added. If the input does not match the name of an already defined chain, a new chain will be created. ### Optional - `address_list` (String) Name of the address list to be used. Applicable if action is add-dst-to-address-list or add-src-to-address-list. - `address_list_timeout` (String) Time interval after which the address will be removed from the address list specified by address-list parameter. Used in conjunction with add-dst-to-address-list or add-src-to-address-list actions. - `comment` (String) - `connection_bytes` (String) Matches packets only if a given amount of bytes has been transfered through the particular connection. - `connection_limit` (String) Matches connections per address or address block after given value is reached. Should be used together with connection-state=new and/or with tcp-flags=syn because matcher is very resource intensive. - `connection_mark` (String) Matches packets marked via mangle facility with particular connection mark. If no-mark is set, rule will match any unmarked connection. - `connection_rate` (String) Connection Rate is a firewall matcher that allow to capture traffic based on present speed of the connection (0..4294967295). - `connection_type` (String) Matches packets from related connections based on information from their connection tracking helpers. - `content` (String) Match packets that contain specified text. - `disabled` (Boolean) - `dscp` (Number) Matches DSCP IP header field. - `dst_address` (String) Matches packets which destination is equal to specified IP or falls into specified IP range. - `dst_address_list` (String) Matches destination address of a packet against user-defined address list. - `dst_address_type` (String) Matches destination address type. - `dst_limit` (String) Matches packets until a given rate is exceeded. - `dst_port` (String) List of destination port numbers or port number ranges. - `fragment` (Boolean) Matches fragmented packets. First (starting) fragment does not count. If connection tracking is enabled there will be no fragments as system automatically assembles every packet - `hotspot` (String) Matches packets received from HotSpot clients against various HotSpot matchers. - `icmp_options` (String) Matches ICMP type: code fields. - `in_bridge_port` (String) Actual interface the packet has entered the router if the incoming interface is a bridge. Works only if use-ip-firewall is enabled in bridge settings. - `in_bridge_port_list` (String) Set of interfaces defined in interface list. Works the same as in-bridge-port. - `in_interface` (String) Interface the packet has entered the router. - `in_interface_list` (String) Set of interfaces defined in interface list. Works the same as in-interface. - `ingress_priority` (Number) Matches the priority of an ingress packet. Priority may be derived from VLAN, WMM, DSCP, or MPLS EXP bit. - `ipsec_policy` (String) Matches the policy used by IPsec. Value is written in the following format: direction, policy. - `ipv4_options` (String) Matches IPv4 header options. - `jump_target` (String) Name of the target chain to jump to. Applicable only if action=jump. - `layer7_protocol` (String) Layer7 filter name. - `limit` (String) Matches packets up to a limited rate (packet rate or bit rate). A rule using this matcher will match until this limit is reached. Parameters are written in the following format: rate[/time],burst:mode. - `log` (Boolean) Add a message to the system log. - `log_prefix` (String) Adds specified text at the beginning of every log message. Applicable if action=log or log=yes configured. - `nth` (String) Matches every nth packet: nth=2,1 rule will match every first packet of 2, hence, 50% of all the traffic that is matched by the rule - `out_bridge_port` (String) Actual interface the packet is leaving the router if the outgoing interface is a bridge. Works only if use-ip-firewall is enabled in bridge settings. - `out_bridge_port_list` (String) Set of interfaces defined in interface list. Works the same as out-bridge-port. - `out_interface` (String) Interface the packet is leaving the router. - `out_interface_list` (String) Set of interfaces defined in interface list. Works the same as out-interface. - `packet_mark` (String) Matches packets marked via mangle facility with particular packet mark. If no-mark is set, the rule will match any unmarked packet. - `packet_size` (String) Matches packets of specified size or size range in bytes. - `per_connection_classifier` (String) PCC matcher allows dividing traffic into equal streams with the ability to keep packets with a specific set of options in one particular stream. - `place_before` (String) Before which position the rule will be inserted. > Please check the effect of this option, as it does not work as you think! > Best way to use in conjunction with a data source. See [example](../data-sources/ip_firewall.md#example-usage). - `port` (String) Matches if any (source or destination) port matches the specified list of ports or port ranges. Applicable only if protocol is TCP or UDP - `priority` (Number) Matches the packet's priority after a new priority has been set. Priority may be derived from VLAN, WMM, DSCP, MPLS EXP bit, or from the priority that has been set using the set-priority action. - `protocol` (String) Matches particular IP protocol specified by protocol name or number. - `psd` (String) Attempts to detect TCP and UDP scans. Parameters are in the following format WeightThreshold, DelayThreshold, LowPortWeight, HighPortWeight. - `random` (Number) Matches packets randomly with a given probability. - `randomise_ports` (Boolean) Randomize to which public port connections will be mapped. - `routing_mark` (String) Matches packets marked by mangle facility with particular routing mark. - `same_not_by_dst` (Boolean) Specifies whether to take into account or not destination IP address when selecting a new source IP address. Applicable if action=same - `socks5_port` (Number) Listening port of the SOCKS5 proxy server. - `socks5_server` (String) IP address of the SOCKS5 proxy server. (only IPv4 addresses are supported) - `socksify_service` (String) Name of existing socksify service. - `src_address` (String) Matches packets which source is equal to specified IP or falls into a specified IP range. - `src_address_list` (String) Matches source address of a packet against user-defined address list. - `src_address_type` (String) Matches source address type. - `src_mac_address` (String) Matches source MAC address of the packet. - `src_port` (String) List of source ports and ranges of source ports. Applicable only if a protocol is TCP or UDP. - `tcp_mss` (String) Matches TCP MSS value of an IP packet. - `time` (String) Allows to create a filter based on the packets' arrival time and date or, for locally generated packets, departure time and date. - `to_addresses` (String) Replace original address with specified one. Applicable if action is dst-nat, netmap, same, src-nat. - `to_ports` (String) Replace the original port with the specified one. Applicable if action is dst-nat, redirect, masquerade, netmap, same, src-nat. - `ttl` (String) Matches packets TTL value. ### Read-Only - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. - `invalid` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/firewall/nat get [print show-ids]] terraform import routeros_ip_firewall_nat.rule "*0" #Or you can import a resource using one of its attributes terraform import routeros_ip_firewall_nat.rule "name=xxx" ``` ================================================ FILE: docs/resources/ip_firewall_raw.md ================================================ # routeros_ip_firewall_raw (Resource) ## Schema ### Required - `action` (String) Action to take if a packet is matched by the rule - `chain` (String) Specifies to which chain rule will be added. If the input does not match the name of an already defined chain, a new chain will be created. ### Optional - `address_list` (String) Name of the address list used in 'add-dst-to-address-list' and 'add-src-to-address-list' actions. - `address_list_timeout` (String) Time interval after which the address will be removed from the address list specified by address-list parameter. Used in conjunction with add-dst-to-address-list or add-src-to-address-list actions. - `comment` (String) - `content` (String) Match packets that contain specified text. - `disabled` (Boolean) - `dscp` (Number) Matches DSCP IP header field. - `dst_address` (String) Matches packets which destination is equal to specified IP or falls into specified IP range. - `dst_address_list` (String) Matches destination address of a packet against user-defined address list. - `dst_address_type` (String) Matches destination address type. - `dst_limit` (String) Matches packets until a given rate is exceeded. - `dst_port` (String) List of destination port numbers or port number ranges. - `fragment` (Boolean) Matches fragmented packets. First (starting) fragment does not count. If connection tracking is enabled there will be no fragments as system automatically assembles every packet - `hotspot` (String) Matches packets received from HotSpot clients against various HotSpot matchers. - `icmp_options` (String) Matches ICMP type: code fields. - `in_bridge_port` (String) Actual interface the packet has entered the router if the incoming interface is a bridge. Works only if use-ip-firewall is enabled in bridge settings. - `in_bridge_port_list` (String) Set of interfaces defined in interface list. Works the same as in-bridge-port. - `in_interface` (String) Interface the packet has entered the router. - `in_interface_list` (String) Set of interfaces defined in interface list. Works the same as in-interface. - `ingress_priority` (Number) Matches the priority of an ingress packet. Priority may be derived from VLAN, WMM, DSCP, or MPLS EXP bit. - `ipsec_policy` (String) Matches the policy used by IPsec. Value is written in the following format: direction, policy. - `ipv4_options` (String) Matches IPv4 header options. - `jump_target` (String) Name of the target chain to jump to. Applicable only if action=jump. - `limit` (String) Matches packets up to a limited rate (packet rate or bit rate). A rule using this matcher will match until this limit is reached. Parameters are written in the following format: rate[/time],burst:mode. - `log` (Boolean) Add a message to the system log. - `log_prefix` (String) Adds specified text at the beginning of every log message. Applicable if action=log or log=yes configured. - `nth` (String) Matches every nth packet: nth=2,1 rule will match every first packet of 2, hence, 50% of all the traffic that is matched by the rule - `out_bridge_port` (String) Actual interface the packet is leaving the router if the outgoing interface is a bridge. Works only if use-ip-firewall is enabled in bridge settings. - `out_bridge_port_list` (String) Set of interfaces defined in interface list. Works the same as out-bridge-port. - `out_interface` (String) Interface the packet is leaving the router. - `out_interface_list` (String) Set of interfaces defined in interface list. Works the same as out-interface. - `packet_mark` (String) Matches packets marked via mangle facility with particular packet mark. If no-mark is set, the rule will match any unmarked packet. - `packet_size` (String) Matches packets of specified size or size range in bytes. - `per_connection_classifier` (String) PCC matcher allows dividing traffic into equal streams with the ability to keep packets with a specific set of options in one particular stream. - `place_before` (String) Before which position the rule will be inserted. > Please check the effect of this option, as it does not work as you think! > Best way to use in conjunction with a data source. See [example](../data-sources/ip_firewall.md#example-usage). - `port` (String) Matches if any (source or destination) port matches the specified list of ports or port ranges. Applicable only if protocol is TCP or UDP - `priority` (Number) Matches the packet's priority after a new priority has been set. Priority may be derived from VLAN, WMM, DSCP, MPLS EXP bit, or from the priority that has been set using the set-priority action. - `protocol` (String) Matches particular IP protocol specified by protocol name or number. - `psd` (String) Attempts to detect TCP and UDP scans. Parameters are in the following format WeightThreshold, DelayThreshold, LowPortWeight, HighPortWeight. - `random` (Number) Matches packets randomly with a given probability. - `src_address` (String) Matches packets which source is equal to specified IP or falls into a specified IP range. - `src_address_list` (String) Matches source address of a packet against user-defined address list. - `src_address_type` (String) Matches source address type. - `src_mac_address` (String) Matches source MAC address of the packet. - `src_port` (String) List of source ports and ranges of source ports. Applicable only if a protocol is TCP or UDP. - `tcp_flags` (String) Matches specified TCP flags. - `tcp_mss` (String) Matches TCP MSS value of an IP packet. - `time` (String) Allows to create a filter based on the packets' arrival time and date or, for locally generated packets, departure time and date. - `tls_host` (String) Allows matching HTTPS traffic based on TLS SNI hostname. - `ttl` (String) Matches packets TTL value. ### Read-Only - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. - `invalid` (Boolean) ================================================ FILE: docs/resources/ip_hotspot.md ================================================ # routeros_ip_hotspot (Resource) ## Example Usage ```terraform resource "routeros_ip_hotspot" "test" { name = "server-1" interface = "ether2" } ``` ## Schema ### Required - `interface` (String) Interface to run HotSpot on. - `name` (String) HotSpot server's name or identifier. ### Optional - `address_pool` (String) Address space used to change HotSpot client any IP address to a valid address. Useful for providing public network access to mobile clients that are not willing to change their networking settings. - `addresses_per_mac` (String) Number of IP addresses allowed to be bind with the MAC address, when multiple HotSpot clients connected with one MAC-address. - `disabled` (Boolean) - `idle_timeout` (String) Period of inactivity for unauthorized clients. When there is no traffic from this client (literally client computer should be switched off), once the timeout is reached, a user is dropped from the HotSpot host list, its used address becomes available. - `keepalive_timeout` (String) The exact value of the keepalive-timeout, that is applied to the user. Value shows how long the host can stay out of reach to be removed from the HotSpot. - `login_timeout` (String) Period of time after which if a host hasn't been authorized itself with a system the host entry gets deleted from host table. Loop repeats until the host logs in the system. Enable if there are situations where a host cannot log in after being too long in the host table unauthorized. - `profile` (String) HotSpot server default HotSpot profile, which is located in `/ip/hotspot/profile`. ### Read-Only - `id` (String) The ID of this resource. - `invalid` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/hotspot get [print show-ids]] terraform import routeros_ip_hotspot.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_hotspot.test "name=xxx" ``` ================================================ FILE: docs/resources/ip_hotspot_ip_binding.md ================================================ # routeros_ip_hotspot_ip_binding (Resource) ## Example Usage ```terraform resource "routeros_ip_hotspot_ip_binding" "test" { address = "0.0.0.1" comment = "comment" mac_address = "00:00:00:00:01:10" to_address = "0.0.0.2" } ``` ## Schema ### Optional - `address` (String) The original IP address of the client. - `comment` (String) - `disabled` (Boolean) - `mac_address` (String) MAC address of the client. - `server` (String) Name of the HotSpot server. `all` - will be applied to all hotspot servers. - `to_address` (String) New IP address of the client, translation occurs on the router (client does not know anything about the translation). - `type` (String) Type of the IP-binding action * regular - performs One-to-One NAT according to the rule, translates the address to to-address; * bypassed - performs the translation, but excludes client from login to the HotSpot; * blocked - translation is not performed and packets from a host are dropped. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/hotspot/ip-binding get [print show-ids]] terraform import routeros_ip_hotspot_ip_binding.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_hotspot_ip_binding.test "name=xxx" ``` ================================================ FILE: docs/resources/ip_hotspot_profile.md ================================================ # routeros_ip_hotspot_profile (Resource) ## Example Usage ```terraform resource "routeros_ip_hotspot_profile" "test" { name = "hsprof-1" login_by = ["mac", "https", "trial"] use_radius = true } ``` ## Schema ### Required - `name` (String) Descriptive name of the profile. ### Optional - `dns_name` (String) DNS name of the HotSpot server (it appears as the location of the login page). This name will automatically be added as a static DNS entry in the DNS cache. Name can affect if Hotspot is automatically detected by client device. For example, iOS devices may not detect Hotspot that has a name which includes `.local`. - `hotspot_address` (String) IP address of HotSpot service. - `html_directory` (String) Directory name in which HotSpot HTML pages are stored (by default hotspot directory). It is possible to specify different directory with modified HTML pages. To change HotSpot login page, connect to the router with FTP and download hotspot directory contents. v6.31 and older software builds: For devices where `flash` directory is present, hotspot html directory must be stored there and path must be typed in as follows: `/(hotspot_dir)`. This must be done in this order as hotspot sees `flash` directory as root location. v6.32 and newer software builds: full path must be typed in html-directory field, including `/flash/(hotspot_dir)`. - `html_directory_override` (String) Alternative path for hotspot html files. It should be used only if customized hotspot html files are stored on external storage(attached usb, hdd, etc). If configured then hotspot will switch to this html path as soon at it becomes available and switch back to html-directory path if override path becomes non-available for some reason. - `http_cookie_lifetime` (String) HTTP cookie validity time, the option is related to cookie HotSpot login method. - `http_proxy` (String) Address and port of the proxy server for HotSpot service, when default value is used all request are resolved by the local `/ip proxy`. - `https_redirect` (Boolean) Whether to redirect unauthenticated user to hotspot login page, if he is visiting a https:// url. Since certificate domain name will mismatch, often this leads to errors, so you can set this parameter to `no` and all https requests will simply be rejected and user will have to visit a http page. - `login_by` (Set of String) Used HotSpot authentication method * mac-cookie - enables login by mac cookie method. * cookie - may only be used with other HTTP authentication method. HTTP cookie is generated, when user authenticates in HotSpot for the first time. User is not asked for the login/password and authenticated automatically, until cookie-lifetime is active. * http-chap - login/password is required for the user to authenticate in HotSpot. CHAP challenge-response method with MD5 hashing algorithm is used for protecting passwords. * http-pap - login/password is required for user to authenticate in HotSpot. Username and password are sent over network in plain text. * https - login/password is required for user to authenticate in HotSpot. Client login/password exchange between client and server is encrypted with SSL tunnel. * mac - client is authenticated without asking login form. Client MAC-address is added to `/ip hotspot user` database, client is authenticated as soon as connected to the HotSpot * trial - client is allowed to use internet without HotSpot login for the specified amount of time. - `mac_auth_mode` (String) Allows to control User-Name and User-Password RADIUS attributes when using MAC authentication. - `mac_auth_password` (String, Sensitive) Used together with MAC authentication, field used to specify password for the users to be authenticated by their MAC addresses. The following option is required, when specific RADIUS server rejects authentication for the clients with blank password. - `nas_port_type` (String) `NAS-Port-Type` value to be sent to RADIUS server, `NAS-Port-Type` values are described in the RADIUS RFC 2865. This optional value attribute indicates the type of the physical port of the HotSpot server. - `radius_accounting` (Boolean) Send RADIUS server accounting information for each user, when yes is used. - `radius_default_domain` (String) Default domain to use for RADIUS requests. Allows to use separate RADIUS server per `/ip hotspot profile`. If used, same domain name should be specified under `/radius domain` value. - `radius_interim_update` (String) How often to send accounting updates . When received is set, interim-time is used from RADIUS server. 0s is the same as received. - `radius_location_id` (String) `RADIUS-Location-Id` property. - `radius_location_name` (String) `RADIUS-Location-Id` to be sent to RADIUS server. Used to identify location of the HotSpot server during the communication with RADIUS server. Value is optional and used together with RADIUS server. - `radius_mac_format` (String) Controls how the MAC address of the client is encoded in the `User-Name` and `User-Password` attributes when using MAC authentication. - `rate_limit` (String) Rate limitation in form of rx-rate[/tx-rate] [rx-burst-rate[/tx-burst-rate] [rx-burst-threshold[/tx-burst-threshold] [rx-burst-time[/tx-burst-time]]]] [priority] [rx-rate-min[/tx-rate-min]] from the point of view of the router (so `rx` is client upload, and `tx` is client download). All rates should be numbers with optional 'k' (1,000s) or 'M' (1,000,000s). If tx-rate is not specified, rx-rate is as tx-rate too. Same goes for tx-burst-rate and tx-burst-threshold and tx-burst-time. If both rx-burst-threshold and tx-burst-threshold are not specified (but burst-rate is specified), rx-rate and tx-rate is used as burst thresholds. If both rx-burst-time and tx-burst-time are not specified, 1s is used as default. rx-rate-min and tx-rate min are the values of limit-at properties. - `smtp_server` (String) SMTP server address to be used to redirect HotSpot users SMTP requests. - `split_user_domain` (Boolean) Split username from domain name when the username is given in `user@domain` or in `domain\user` format from RADIUS server. - `ssl_certificate` (String) Name of the SSL certificate on the router to to use only for HTTPS authentication. - `trial_uptime_limit` (String) Used only with trial authentication method. Time value specifies, how long trial user identified by MAC address can use access to public networks without HotSpot authentication. - `trial_uptime_reset` (String) Used only with trial authentication method. - `trial_user_profile` (String) Specifies hotspot user profile for trial users. - `use_radius` (Boolean) Use RADIUS to authenticate HotSpot users. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/hotspot/profile get [print show-ids]] terraform import routeros_ip_hotspot_profile.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_hotspot_profile.test "name=xxx" ``` ================================================ FILE: docs/resources/ip_hotspot_service_port.md ================================================ # routeros_ip_hotspot_service_port (Resource) ## Example Usage ```terraform resource "routeros_ip_hotspot_service_port" "test" { name = "ftp" disabled = true } ``` ## Schema ### Required - `name` (String) Service name. ### Optional - `disabled` (Boolean) ### Read-Only - `id` (String) The ID of this resource. - `ports` (String) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/hotspot/service-port get [print show-ids]] terraform import routeros_ip_hotspot_service_port.test *1 #Or you can import a resource using one of its attributes terraform import routeros_ip_hotspot_service_port.test "name=xxx" ``` ================================================ FILE: docs/resources/ip_hotspot_user.md ================================================ # routeros_ip_hotspot_user (Resource) ## Example Usage ```terraform resource "routeros_ip_hotspot_user" "test" { name = "user-1" } ``` ## Schema ### Required - `name` (String) HotSpot login page username, when MAC-address authentication is used name is configured as client's MAC-address. ### Optional - `address` (Number) IP address, when specified client will get the address from the HotSpot one-to-one NAT translations. Address does not restrict HotSpot login only from this address. - `comment` (String) - `disabled` (Boolean) - `email` (String) HotSpot client's e-mail, informational value for the HotSpot user. - `limit_bytes_in` (Number) Maximal amount of bytes that can be received from the user. User is disconnected from HotSpot after the limit is reached. - `limit_bytes_out` (Number) Maximal amount of bytes that can be transmitted from the user. User is disconnected from HotSpot after the limit is reached. - `limit_bytes_total` (Number) (limit-bytes-in+limit-bytes-out). User is disconnected from HotSpot after the limit is reached. - `limit_uptime` (Number) Uptime limit for the HotSpot client, user is disconnected from HotSpot as soon as uptime is reached. - `mac_address` (Number) Client is allowed to login only from the specified MAC-address. If value is 00:00:00:00:00:00, any mac address is allowed. - `password` (String, Sensitive) User password. - `profile` (String) User profile configured in `/ip hotspot user profile`. - `routes` (String) Routes added to HotSpot gateway when client is connected. The route format dst-address gateway metric (for example, `192.168.1.0/24 192.168.0.1 1`). - `server` (String) HotSpot server's name to which user is allowed login. ### Read-Only - `default` (Boolean) It's the default item. - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/hotspot/user get [print show-ids]] terraform import routeros_ip_hotspot_user.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_hotspot_user.test "name=xxx" ``` ================================================ FILE: docs/resources/ip_hotspot_user_profile.md ================================================ # routeros_ip_hotspot_user_profile (Resource) ## Example Usage ```terraform resource "routeros_ip_hotspot_user_profile" "test" { add_mac_cookie = true address_list = "list-1" idle_timeout = "none" keepalive_timeout = "2m" mac_cookie_timeout = "3d" name = "new-profile" shared_users = 3 status_autorefresh = "2m" transparent_proxy = true advertise = true } ``` ## Schema ### Required - `name` (String) Descriptive name of the profile. ### Optional - `add_mac_cookie` (Boolean) Allows to add mac cookie for users. - `address_list` (String) Name of the address list in which users IP address will be added. Useful to mark traffic per user groups for queue tree configurations. - `address_pool` (String) IP pool name from which the user will get IP. When user has improper network settings configuration on the computer, HotSpot server makes translation and assigns correct IP address from the pool instead of incorrect one. - `advertise` (Boolean) Enable forced advertisement popups. After certain interval specific web-page is being displayed for HotSpot users. Advertisement page might be blocked by browsers popup blockers. - `advertise_interval` (Set of String) Set of intervals between advertisement popups. After the list is done, the last value is used for all further advertisements, 10 minutes. - `advertise_timeout` (String) How long advertisement is shown, before blocking network access for HotSpot client. Connection to Internet is not allowed, when advertisement is not shown. - `advertise_url` (String) List of URLs that is show for advertisement popups. After the last URL is used, list starts from the begining. - `idle_timeout` (String) Maximal period of inactivity for authorized HotSpot clients. Timer is counting, when there is no traffic coming from that client and going through the router, for example computer is switched off. User is logged out, dropped of the host list, the address used by the user is freed, when timeout is reached. - `incoming_filter` (String) Name of the firewall chain applied to incoming packets from the users of this profile, jump rule is required from built-in chain (input, forward, output) to chain=hotspot. - `incoming_packet_mark` (String) Packet mark put on incoming packets from every user of this profile. - `insert_queue_before` (String) - `keepalive_timeout` (String) Keepalive timeout for authorized HotSpot clients. Used to detect, that the computer of the client is alive and reachable. User is logged out, when timeout value is reached. - `mac_cookie_timeout` (String) Selects mac-cookie timeout from last login or logout. Read more>>. - `on_login` (String) Script name to be executed, when user logs in to the HotSpot from the particular profile. It is possible to get username from internal user and interface variable. For example, :log info ``User $user logged in!`` . If hotspot is set on bridge interface, then interface variable will show bridge as actual interface unless use-ip-firewall' is set in bridge settings. List of available variables: $user $username (alternative var name for $user) $address $``mac-address`` $interface. - `on_logout` (String) Script name to be executed, when user logs out from the HotSpot.It is possible to get username from internal user and interface variable. For example, :log info ``User $user logged in!`` . If hotspot is set on bridge interface, then interface variable will show bridge as actual interface unless use-ip-firewall is set in bridge settings. List of available variables: $user $username (alternative var name for $user) $address $``mac-address`` $interface $cause Starting with v6.34rc11 some additional variables are available: $uptime-secs - final session time in seconds $bytes-in - bytes uploaded $bytes-out - bytes downloaded $bytes-total - bytes up + bytes down $packets-in - packets uploaded $packets-out - packets downloaded $packets-total - packets up + packets down. - `open_status_page` (String) Option to show status page for user authenticated with mac login method. For example to show advertisement on status page (alogin.html) http-login - open status page only for HTTP login (includes cookie and HTTPS) always - open HTTP status page in case of mac login as well. - `outgoing_filter` (String) Name of the firewall chain applied to outgoing packets from the users of this profile, jump rule is required from built-in chain (input, forward, output) to chain=hotspot. - `outgoing_packet_mark` (String) Packet mark put on outgoing packets from every user of this profile. - `parent_queue` (String) - `queue_type` (String) - `rate_limit` (String) Simple dynamic queue is created for user, once it logs in to the HotSpot. Rate-limitation is configured in the following form [rx-rate[/tx-rate] [rx-burst-rate[/tx-burst-rate] [rx-burst-threshold[/tx-burst-threshold] [rx-burst-time[/tx-burst-time] [priority] [rx-rate-min[/tx-rate-min]]]]. For example, to set 1M download, 512k upload for the client, rate-limit=512k/1M. - `session_timeout` (String) Allowed session time for client. After this time, the user is logged out unconditionally. - `shared_users` (String) Allowed number of simultaneously logged in users with the same HotSpot username. - `status_autorefresh` (String) HotSpot status page autorefresh interval. - `transparent_proxy` (Boolean) Use transparent HTTP proxy for the authorized users of this profile. ### Read-Only - `default` (Boolean) It's the default item. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/hotspot/user/profile get [print show-ids]] terraform import routeros_ip_hotspot_user_profile.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_hotspot_user_profile.test "name=xxx" ``` ================================================ FILE: docs/resources/ip_hotspot_walled_garden.md ================================================ # routeros_ip_hotspot_walled_garden (Resource) ## Example Usage ```terraform resource "routeros_ip_hotspot_walled_garden" "test" { action = "deny" dst_host = "1.2.3.4" dst_port = "!443" } ``` ## Schema ### Optional - `action` (String) Action to perform, when packet matches the rule `allow` - allow access to the web-page without authorization, `deny` - the authorization is required to access the web-page. - `comment` (String) - `disabled` (Boolean) - `dst_host` (String) Domain name of the destination web-server. - `dst_port` (String) TCP port number, client sends request to. - `method` (String) HTTP method of the request. - `path` (String) The path of the request, path comes after `http://dst_host/`. - `server` (String) Name of the HotSpot server, rule is applied to. - `src_address` (String) Source address of the user, usually IP address of the HotSpot client. ### Read-Only - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/hotspot/walled-garden get [print show-ids]] terraform import routeros_ip_hotspot_walled_garden.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_hotspot_walled_garden.test "name=xxx" ``` ================================================ FILE: docs/resources/ip_hotspot_walled_garden_ip.md ================================================ # routeros_ip_hotspot_walled_garden_ip (Resource) ## Example Usage ```terraform resource "routeros_ip_hotspot_walled_garden_ip" "test" { action = "reject" dst_address = "!0.0.0.0" dst_address_list = "dlist" dst_port = "0-65535" protocol = "tcp" src_address = "0.0.0.0" src_address_list = "slist" } ``` ## Schema ### Optional - `action` (String) Action to perform, when packet matches the rule allow - allow access to the web-page without authorization deny - the authorization is required to access the web-page reject - the authorization is required to access the resource, ICMP reject message will be sent to client, when packet will match the rule. - `comment` (String) - `disabled` (Boolean) - `dst_address` (String) Destination IP address, IP address of the WEB-server. Ignored if dst-host is already specified. - `dst_address_list` (String) Destination IP address list. Ignored if dst-host is already specified. - `dst_host` (String) Domain name of the destination web-server. When this parameter is specified dynamic entry is added to Walled Garden. - `dst_port` (String) TCP port number, client sends request to. - `protocol` (String) IP protocol. - `server` (String) Name of the HotSpot server, rule is applied to. - `src_address` (String) Source address of the user, usually IP address of the HotSpot client. - `src_address_list` (String) Source IP address list. ### Read-Only - `id` (String) The ID of this resource. - `invalid` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/hotspot/walled-garden/ip get [print show-ids]] terraform import routeros_ip_hotspot_walled_garden_ip.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_hotspot_walled_garden_ip.test "name=xxx" ``` ================================================ FILE: docs/resources/ip_ipsec_identity.md ================================================ # routeros_ip_ipsec_identity (Resource) ## Example Usage ```terraform resource "routeros_ip_ipsec_mode_config" "test" { name = "NordVPN" responder = false } resource "routeros_ip_ipsec_peer" "test" { address = "lv20.nordvpn.com" exchange_mode = "ike2" name = "NordVPN" } resource "routeros_ip_ipsec_identity" "test" { auth_method = "eap" certificate = "" eap_methods = "eap-mschapv2" generate_policy = "port-strict" mode_config = routeros_ip_ipsec_mode_config.test.name peer = routeros_ip_ipsec_peer.test.name username = "support@mikrotik.com" password = "secret" } ``` ## Schema ### Required - `peer` (String) Name of the peer on which the identity applies. ### Optional - `auth_method` (String) Authentication method: * digital-signature - authenticate using a pair of RSA certificates; * eap - IKEv2 EAP authentication for initiator (peer with a netmask of `/32`). Must be used together with eap-methods; * eap-radius - IKEv2 EAP RADIUS passthrough authentication for the responder (RFC 3579). A server certificate in this case is required. If a server certificate is not specified then only clients supporting EAP-only (RFC 5998) will be able to connect. Note that the EAP method should be compatible with EAP-only; * pre-shared-key - authenticate by a password (pre-shared secret) string shared between the peers (not recommended since an offline attack on the pre-shared key is possible); * rsa-key - authenticate using an RSA key imported in keys menu. Only supported in IKEv1; * pre-shared-key-xauth - authenticate by a password (pre-shared secret) string shared between the peers + XAuth username and password. Only supported in IKEv1; * rsa-signature-hybrid - responder certificate authentication with initiator XAuth. Only supported in IKEv1. - `certificate` (String) Name of a certificate listed in System/Certificates (signing packets; the certificate must have the private key). Applicable if digital signature authentication method (`auth-method=digital-signature`) or EAP (a`uth-method=eap`) is used. - `comment` (String) - `disabled` (Boolean) - `eap_methods` (String) All EAP methods requires whole certificate chain including intermediate and root CA certificates to be present in System/Certificates menu. Also, the username and password (if required by the authentication server) must be specified. Multiple EAP methods may be specified and will be used in a specified order. Currently supported EAP methods: * eap-mschapv2; * eap-peap - also known as PEAPv0/EAP-MSCHAPv2; * eap-tls - requires additional client certificate specified under certificate parameter; * eap-ttls. - `generate_policy` (String) Allow this peer to establish SA for non-existing policies. Such policies are created dynamically for the lifetime of SA. Automatic policies allows, for example, to create IPsec secured L2TP tunnels, or any other setup where remote peer's IP address is not known at the configuration time. `no` - do not generate policies; `port-override` - generate policies and force policy to use any port (old behavior); `port-strict` - use ports from peer's proposal, which should match peer's policy. - `key` (String) Name of the private key from keys menu. Applicable if RSA key authentication method (`auth-method=rsa-key`) is used. - `match_by` (String) Defines the logic used for peer's identity validation. `remote-id` - will verify the peer's ID according to remote-id setting. `certificate` will verify the peer's certificate with what is specified under remote-certificate setting. - `mode_config` (String) Name of the configuration parameters from mode-config menu. When parameter is set mode-config is enabled. - `my_id` (String) On initiator, this controls what ID_i is sent to the responder. On responder, this controls what ID_r is sent to the initiator. In IKEv2, responder also expects this ID in received ID_r from initiator. `auto` - tries to use correct ID automatically: IP for pre-shared key, SAN (DN if not present) for certificate based connections; `address` - IP address is used as ID;dn - the binary Distinguished Encoding Rules (DER) encoding of an ASN.1 X.500 Distinguished Name; `fqdn` - fully qualified domain name; `key-id` - use the specified key ID for the identity; `user-fqdn` - specifies a fully-qualified username string, for example, `user@domain.com`. - `notrack_chain` (String) Adds IP/Firewall/Raw rules matching IPsec policy to a specified chain. Use together with generate-policy. - `password` (String, Sensitive) XAuth or EAP password. Applicable if pre-shared key with XAuth authentication method (`auth-method=pre-shared-key-xauth`) or EAP (`auth-method=eap`) is used. - `policy_template_group` (String) If generate-policy is enabled, traffic selectors are checked against templates from the same group. If none of the templates match, Phase 2 SA will not be established. - `remote_certificate` (String) Name of a certificate (listed in `System/Certificates`) for authenticating the remote side (validating packets; no private key required). If a remote-certificate is not specified then the received certificate from a remote peer is used and checked against CA in the certificate menu. Proper CA must be imported in a certificate store. If remote-certificate and match-by=certificate is specified, only the specific client certificate will be matched. Applicable if digital signature authentication method (`auth-method=digital-signature`) is used. - `remote_id` (String) This parameter controls what ID value to expect from the remote peer. Note that all types except for ignoring will verify remote peer's ID with a received certificate. In case when the peer sends the certificate name as its ID, it is checked against the certificate, else the ID is checked against Subject Alt. Name. `auto` - accept all ID's;address - IP address is used as ID;dn - the binary Distinguished Encoding Rules (DER) encoding of an ASN.1 X.500 Distinguished Name; `fqdn` - fully qualified domain name. Only supported in IKEv2; `user-fqdn` - a fully-qualified username string, for example, `user@domain.com`. Only supported in IKEv2; `key-id` - specific key ID for the identity. Only supported in IKEv2; `ignore` - do not verify received ID with certificate (dangerous). * Wildcard key ID matching **is not supported**, for example `remote-id=`key-id:CN=*.domain.com`. - `remote_key` (String) Name of the public key from keys menu. Applicable if RSA key authentication method (`auth-method=rsa-key`) is used. - `secret` (String, Sensitive) Secret string. If it starts with '0x', it is parsed as a hexadecimal value. Applicable if pre-shared key authentication method (`auth-method=pre-shared-key` and `auth-method=pre-shared-key-xauth`) is used. - `username` (String) XAuth or EAP username. Applicable if pre-shared key with XAuth authentication method (`auth-method=pre-shared-key-xauth`) or EAP (`auth-method=eap`) is used. ### Read-Only - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/ipsec/identity get [print show-ids]] terraform import routeros_ip_ipsec_identity.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_ipsec_identity.test "peer=NordVPN" ``` ================================================ FILE: docs/resources/ip_ipsec_key.md ================================================ # routeros_ip_ipsec_key (Resource) ## Example Usage ```terraform resource "routeros_ip_ipsec_key" "test" { name = "test-key" key_size = 2048 } ``` ## Schema ### Required - `key_size` (Number) Size of this key. - `name` (String) ### Optional ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/ipsec/key get [print show-ids]] terraform import routeros_ip_ipsec_key.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_ipsec_key.test "name=test-key" ``` ================================================ FILE: docs/resources/ip_ipsec_mode_config.md ================================================ # routeros_ip_ipsec_mode_config (Resource) ## Example Usage ```terraform resource "routeros_ip_ipsec_mode_config" "test" { name = "test-cfg" address = "1.2.3.4" split_include = ["0.0.0.0/0"] split_dns = ["1.1.1.1"] } ``` ## Schema ### Required - `name` (String) ### Optional - `address` (String) Single IP address for the initiator instead of specifying a whole address pool. - `address_pool` (String) Name of the address pool from which the responder will try to assign address if mode-config is enabled. - `address_prefix_length` (Number) Prefix length (netmask) of the assigned address from the pool. - `connection_mark` (String) Firewall connection mark. - `responder` (Boolean) Specifies whether the configuration will work as an initiator (client) or responder (server). The initiator will request for mode-config parameters from the responder. - `split_dns` (Set of String) List of DNS names that will be resolved using a system-dns=yes or static-dns= setting. - `split_include` (Set of String) List of subnets in CIDR format, which to tunnel. Subnets will be sent to the peer using the CISCO UNITY extension, a remote peer will create specific dynamic policies. - `src_address_list` (String) Specifying an address list will generate dynamic source NAT rules. This parameter is only available with responder=no. A roadWarrior client with NAT. - `static_dns` (String) Manually specified DNS server's IP address to be sent to the client. - `system_dns` (Boolean) When this option is enabled DNS addresses will be taken from `/ip dns`. - `use_responder_dns` (String) ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/ipsec/mode/config get [print show-ids]] terraform import routeros_ip_ipsec_mode_config.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_ipsec_mode_config.test "address=1.2.3.4" ``` ================================================ FILE: docs/resources/ip_ipsec_peer.md ================================================ # routeros_ip_ipsec_peer (Resource) ## Example Usage ```terraform resource "routeros_ip_ipsec_peer" "test" { address = "lv20.nordvpn.com" exchange_mode = "ike2" name = "NordVPN" } ``` ## Schema ### Required - `name` (String) Peer name. ### Optional - `address` (String) If the remote peer's address matches this prefix, then the peer configuration is used in authentication and establishment of Phase 1. If several peer's addresses match several configuration entries, the most specific one (i.e. the one with the largest netmask) will be used. - `comment` (String) - `disabled` (Boolean) - `exchange_mode` (String) Different ISAKMP phase 1 exchange modes according to RFC 2408. the main mode relaxes rfc2409 section 5.4, to allow pre-shared-key authentication in the main mode. ike2 mode enables Ikev2 RFC 7296. Parameters that are ignored by IKEv2 proposal-check, compatibility-options, lifebytes, dpd-maximum-failures, nat-traversal. - `local_address` (String) Routers local address on which Phase 1 should be bounded to. - `passive` (Boolean) When a passive mode is enabled will wait for a remote peer to initiate an IKE connection. The enabled passive mode also indicates that the peer is xauth responder, and disabled passive mode - xauth initiator. When a passive mode is a disabled peer will try to establish not only phase1 but also phase2 automatically, if policies are configured or created during the phase1. - `port` (Number) Communication port used (when a router is an initiator) to connect to remote peer in cases if remote peer uses the non-default port. - `profile` (String) Name of the profile template that will be used during IKE negotiation. - `send_initial_contact` (Boolean) Specifies whether to send `initial contact` IKE packet or wait for remote side, this packet should trigger the removal of old peer SAs for current source address. Usually, in road warrior setups clients are initiators and this parameter should be set to no. Initial contact is not sent if modecfg or xauth is enabled for ikev1. ### Read-Only - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. - `responder` (Boolean) Whether this peer will act as a responder only (listen to incoming requests) and not initiate a connection. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/ipsec/peer get [print show-ids]] terraform import routeros_ip_ipsec_peer.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_ipsec_peer.test "name=NordVPN" ``` ================================================ FILE: docs/resources/ip_ipsec_policy.md ================================================ # routeros_ip_ipsec_policy (Resource) ## Example Usage ```terraform resource "routeros_ip_ipsec_policy_group" "group-for-policy" { name = "test-group" } resource "routeros_ip_ipsec_policy" "policy" { dst_address = "0.0.0.0/0" group = routeros_ip_ipsec_policy_group.group-for-policy.name proposal = "NordVPN" src_address = "0.0.0.0/0" template = true } ``` ## Schema ### Optional - `action` (String) Specifies what to do with the packet matched by the policy.none - pass the packet unchanged.discard - drop the packet.encrypt - apply transformations specified in this policy and it's SA. - `comment` (String) - `disabled` (Boolean) - `dst_address` (String) Destination address to be matched in packets. Applicable when tunnel mode (`tunnel=yes`) or template (`template=yes`) is used. - `dst_port` (String) Destination port to be matched in packets. If set to any all ports will be matched. - `group` (String) Name of the policy group to which this **template** is assigned. - `ipsec_protocols` (String) Specifies what combination of Authentication Header and Encapsulating Security Payload protocols you want to apply to matched traffic. - `level` (String) Specifies what to do if some of the SAs for this policy cannot be found: * use - skip this transform, do not drop the packet, and do not acquire SA from IKE daemon; * require - drop the packet and acquire SA; * unique - drop the packet and acquire a unique SA that is only used with this particular policy. It is used in setups where multiple clients can sit behind one public IP address (clients behind NAT). - `peer` (String) Name of the peer on which the policy applies. - `proposal` (String) Name of the proposal template that will be sent by IKE daemon to establish SAs for this policy. - `protocol` (String) IP packet protocol to match. - `src_address` (String) Source address to be matched in packets. Applicable when tunnel mode (`tunnel=yes`) or template (`template=yes`) is used. - `src_port` (String) Source port to be matched in packets. If set to any all ports will be matched. - `template` (Boolean) Creates a template and assigns it to a specified policy group.Following parameters are used by template: * group - name of the policy group to which this template is assigned; * src-address, * dst-address - Requested subnet must match in both directions (for example 0.0.0.0/0 to allow all); * protocol - protocol to match, if set to all, then any protocol is accepted; * proposal - SA parameters used for this template; * level - useful when unique is required in setups with multiple clients behind NAT. - `tunnel` (Boolean) Specifies whether to use tunnel mode. ### Read-Only - `active` (Boolean) - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. - `invalid` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/ipsec/policy get [print show-ids]] terraform import routeros_ip_ipsec_policy.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_ipsec_policy.test "group=test-group" ``` ================================================ FILE: docs/resources/ip_ipsec_policy_group.md ================================================ # routeros_ip_ipsec_policy_group (Resource) ## Example Usage ```terraform resource "routeros_ip_ipsec_policy_group" "test" { name = "test-group" } ``` ## Schema ### Required - `name` (String) ### Optional - `comment` (String) ### Read-Only - `default` (Boolean) It's the default item. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/ipsec/policy/group get [print show-ids]] terraform import routeros_ip_ipsec_policy_group.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_ipsec_policy_group.test "name=test-group" ``` ================================================ FILE: docs/resources/ip_ipsec_profile.md ================================================ # routeros_ip_ipsec_profile (Resource) ## Example Usage ```terraform resource "routeros_ip_ipsec_profile" "test" { name = "test-profile" hash_algorithm = "sha256" enc_algorithm = ["aes-192", "aes-256"] dh_group = ["ecp384", "ecp521"] nat_traversal = false } ``` ## Schema ### Required - `name` (String) ### Optional - `dh_group` (Set of String) Diffie-Hellman group (cipher strength). - `dpd_interval` (String) Dead peer detection interval. If set to disable-dpd, dead peer detection will not be used. - `dpd_maximum_failures` (Number) Maximum count of failures until peer is considered to be dead. Applicable if DPD is enabled. - `enc_algorithm` (Set of String) List of encryption algorithms that will be used by the peer. - `hash_algorithm` (String) Hashing algorithm. SHA (Secure Hash Algorithm) is stronger, but slower. MD5 uses 128-bit key, sha1-160bit key. - `lifebytes` (Number) Phase 1 lifebytes is used only as administrative value which is added to proposal. Used in cases if remote peer requires specific lifebytes value to establish phase 1. - `lifetime` (String) Phase 1 lifetime: specifies how long the SA will be valid. - `nat_traversal` (Boolean) Use Linux NAT-T mechanism to solve IPsec incompatibility with NAT routers between IPsec peers. This can only be used with ESP protocol (AH is not supported by design, as it signs the complete packet, including the IP header, which is changed by NAT, rendering AH signature invalid). The method encapsulates IPsec ESP traffic into UDP streams in order to overcome some minor issues that made ESP incompatible with NAT. - `prf_algorithm` (String) - `proposal_check` (String) Phase 2 lifetime check logic: * claim - take shortest of proposed and configured lifetimes and notify initiator about it * exact - require lifetimes to be the same * obey - accept whatever is sent by an initiator * strict - if the proposed lifetime is longer than the default then reject the proposal otherwise accept a proposed lifetime. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/ipsec/profile get [print show-ids]] terraform import routeros_ip_ipsec_profile.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_ipsec_profile.test "name=test-profile" ``` ================================================ FILE: docs/resources/ip_ipsec_proposal.md ================================================ # routeros_ip_ipsec_proposal (Resource) ## Example Usage ```terraform resource "routeros_ip_ipsec_proposal" "test" { name = "NordVPN" pfs_group = "none" lifetime = "45m10s" } ``` ## Schema ### Required - `name` (String) ### Optional - `auth_algorithms` (Set of String) Allowed algorithms for authorization. SHA (Secure Hash Algorithm) is stronger but slower. MD5 uses a 128-bit key, sha1-160bit key. - `comment` (String) - `disabled` (Boolean) - `enc_algorithms` (Set of String) Allowed algorithms and key lengths to use for SAs. - `lifetime` (String) How long to use SA before throwing it out. - `pfs_group` (String) The diffie-Helman group used for Perfect Forward Secrecy. ### Read-Only - `default` (Boolean) It's the default item. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/ipsec/proposal get [print show-ids]] terraform import routeros_ip_ipsec_proposal.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_ipsec_proposal.test "name=NordVPN" ``` ================================================ FILE: docs/resources/ip_ipsec_settings.md ================================================ # routeros_ip_ipsec_settings (Resource) ## Example Usage ```terraform resource "routeros_ip_ipsec_settings" "test" { xauth_use_radius = true interim_update = "60s" } ``` ## Schema ### Optional - `accounting` (Boolean) Whether to send RADIUS accounting requests to a RADIUS server. Applicable if EAP Radius (`auth-method=eap-radius`) or pre-shared key with XAuth authentication method (`auth-method=pre-shared-key-xauth`) is used. - `interim_update` (String) The interval between each consecutive RADIUS accounting Interim update. Accounting must be enabled. - `xauth_use_radius` (Boolean) Whether to use Radius client for XAuth users or not. Property is only applicable to peers using the IKEv1 exchange mode. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_ip_ipsec_settings.test . ``` ================================================ FILE: docs/resources/ip_nat_pmp.md ================================================ # routeros_ip_nat_pmp (Resource) ## Example Usage ```terraform resource "routeros_ip_nat_pmp" "test" { enabled = true } ``` ## Schema ### Optional - `enabled` (Boolean) Enable NAT-PMP service. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_ip_nat_pmp.test . ``` ================================================ FILE: docs/resources/ip_nat_pmp_interfaces.md ================================================ # routeros_ip_nat_pmp_interfaces (Resource) ## Example Usage ```terraform resource "routeros_ip_nat_pmp_interfaces" "test" { interface = "ether1" type = "external" forced_ip = "0.0.0.0" } ``` ## Schema ### Required - `interface` (String) Interface name on which PMP will be running on ### Optional - `disabled` (Boolean) - `forced_ip` (String) Allow specifying what public IP to use if the external interface has more than one IP available. - `type` (String) NAT-PMP interface type: * external - the interface a global IP address is assigned to * internal - router's local interface the clients are connected to ### Read-Only - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/nat-pmp/interfaces get [print show-ids]] terraform import routeros_ip_nat_pmp_interfaces.test '*1' #Or you can import a resource using one of its attributes terraform import routeros_ip_nat_pmp_interfaces.test "name=xxx" ``` ================================================ FILE: docs/resources/ip_neighbor_discovery_settings.md ================================================ # routeros_ip_neighbor_discovery_settings (Resource) ## Example Usage ```terraform resource "routeros_ip_neighbor_discovery_settings" "test" { discover_interface_list = "static" lldp_med_net_policy_vlan = "1" mode = "tx-and-rx" protocol = [] } ``` ## Schema ### Optional - `discover_interface_list` (String) Interface list on which members the discovery protocol will run on. - `discover_interval` (String) An option to adjust the frequency at which neighbor discovery packets are transmitted. The setting is available since RouterOS version 7.16. - `lldp_dcbx` (Boolean) Whether to send Data Center Bridging Capabilities Exchange Protocol (DCBX) TLVs, which allows to communicate switch QoS settings and capabilities with other neighboring devices using LLDP. **Only applies to CRS3xx, CRS5xx, CCR2116 and CCR2216 devices.** - `lldp_mac_phy_config` (Boolean) Whether to send MAC/PHY Configuration/Status TLV in LLDP, which indicates the interface capabilities, current setting of the duplex status, bit rate, and auto-negotiation. Only applies to the Ethernet interfaces. While TLV is optional in LLDP, it is mandatory when sending LLDP-MED, meaning this TLV will be included when necessary even though the property is configured as disabled. - `lldp_max_frame_size` (Boolean) Whether to send Maximum Frame Size TLV in LLDP, which indicates the maximum frame size capability of the interface in bytes (`l2mtu + 18`). Only applies to the Ethernet interfaces. - `lldp_med_net_policy_vlan` (String) Advertised VLAN ID for LLDP-MED Network Policy TLV. This allows assigning a VLAN ID for LLDP-MED capable devices, such as VoIP phones. The TLV will only be added to interfaces where LLDP-MED capable devices are discovered. Other TLV values are predefined and cannot be changed: * Application Type - Voice * VLAN Type - Tagged * L2 Priority - 0 * DSCP Priority - 0 When used together with the bridge interface, the (R/M)STP protocol should be enabled with protocol-mode setting. Additionally, other neighbor discovery protocols (e.g. CDP) should be excluded using protocol setting to avoid LLDP-MED misconfiguration. - `lldp_poe_power` (Boolean) Two specific TLVs facilitate Power over Ethernet (PoE) management between Power Sourcing Equipment (PSE) and Powered Devices (PD). - `lldp_vlan_info` (Boolean) An option whether to send IEEE 802.1 Organizationally Specific TLVs in LLDP related to VLANs. The setting is available since RouterOS version 7.16. - `mode` (String) Selects the neighbor discovery packet sending and receiving mode. The setting is available since RouterOS version 7.7. - `protocol` (Set of String) List of used discovery protocols. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_ip_neighbor_discovery_settings.test . ``` ================================================ FILE: docs/resources/ip_pool.md ================================================ # routeros_ip_pool (Resource) ## Example Usage ```terraform resource "routeros_ip_pool" "pool" { name = "my_ip_pool" ranges = ["10.0.0.100-10.0.0.200"] } ``` ## Schema ### Required - `name` (String) Changing the name of this resource will force it to be recreated. > The links of other configuration properties to this resource may be lost! > Changing the name of the resource outside of a Terraform will result in a loss of control integrity for that resource! - `ranges` (List of String) IP address list of non-overlapping IP address ranges in form of: ["from1-to1", "from2-to2", ..., "fromN-toN"]. For example, ["10.0.0.1-10.0.0.27", "10.0.0.32-10.0.0.47"] ### Optional - `comment` (String) - `next_pool` (String) When address is acquired from pool that has no free addresses, and next-pool property is set to another pool, then next IP address will be acquired from next-pool. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/pool get [print show-ids]] terraform import routeros_ip_pool.pool "*1" ``` ================================================ FILE: docs/resources/ip_route.md ================================================ # routeros_ip_route (Resource) ## Example Usage ```terraform resource "routeros_ip_route" "a_route" { dst_address = "0.0.0.0/0" gateway = "10.0.0.1" } ``` ## Schema ### Required - `gateway` (String) Array of IP addresses or interface names. Specifies which host or interface packets should be sent to (IP | interface | IP%interface | IP@table[, IP | string, [..]]). ### Optional - `blackhole` (Boolean) It's a blackhole route. If you need to cancel route marking, then simply delete the parameter from the configuration of the TF. The value of the parameter (true or false) has no effect on the MT processing logic. - `check_gateway` (String) Currently used check-gateway option. - `comment` (String) - `disabled` (Boolean) - `distance` (Number) Value used in route selection. Routes with smaller distance value are given preference. - `dst_address` (String) IP prefix of route, specifies destination addresses that this route can be used for. - `pref_src` (String) Which of the local IP addresses to use for locally originated packets that are sent via this route. Value of this property has no effect on forwarded packets. If value of this property is set to IP address that is not local address of this router then the route will be inactive (in ROS v6, ROS v7 allows IP spoofing). - `routing_table` (String) Routing table this route belongs to. - `scope` (Number) Used in nexthop resolution. Route can resolve nexthop only through routes that have scope less than or equal to the target-scope of this route. - `suppress_hw_offload` (Boolean) - `target_scope` (Number) Used in nexthop resolution. This is the maximum value of scope for a route through which a nexthop of this route can be resolved. - `vrf_interface` (String) VRF interface name. ### Read-Only - `active` (Boolean) A flag indicates whether the route is elected as Active and eligible to be added to the FIB. - `connect` (Boolean) The route is directly reachable. - `dhcp` (Boolean) A flag indicates whether the route was added by the DHCP service. - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `ecmp` (Boolean) A flag indicates whether the route is added as an Equal-Cost Multi-Path route in the FIB. - `hw_offloaded` (Boolean) Indicates whether the route is eligible to be hardware offloaded on supported hardware. - `id` (String) The ID of this resource. - `immediate_gw` (String) Shows actual (resolved) gateway and interface that will be used for packet forwarding. - `inactive` (Boolean) - `local_address` (String) Local IP address of the connected network. - `static` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/route get [print show-ids]] terraform import routeros_ip_route.a_route "*0" #Or you can import a resource using one of its attributes terraform import routeros_ip_route.a_route "name=xxx" ``` ================================================ FILE: docs/resources/ip_service.md ================================================ # routeros_ip_service (Resource) ## Example Usage ```terraform locals { tls_service = { "api-ssl" = 8729, "www-ssl" = 443 } disable_service = { "api" = 8728, "ftp" = 21, "telnet" = 23, "www" = 80 } enable_service = { "ssh" = 22, "winbox" = 8291 } } resource "routeros_system_certificate" "tls_cert" { name = "tls-cert" common_name = "Mikrotik Router" days_valid = 3650 key_usage = ["key-cert-sign", "crl-sign", "digital-signature", "key-agreement", "tls-server"] key_size = "prime256v1" sign { } } # terraform state rm 'routeros_ip_service.tls["www-ssl"]' # terraform import 'routeros_ip_service.tls["www-ssl"]' www-ssl resource "routeros_ip_service" "tls" { for_each = local.tls_service numbers = each.key port = each.value certificate = routeros_system_certificate.tls_cert.name tls_version = "only-1.2" disabled = false } resource "routeros_ip_service" "disabled" { for_each = local.disable_service numbers = each.key port = each.value disabled = true } resource "routeros_ip_service" "enabled" { for_each = local.enable_service numbers = each.key port = each.value disabled = false } ``` ## Schema ### Required - `numbers` (String) The name of the service whose settings will be changed ( api, api-ssl, ftp, ssh, telnet, winbox, www, www-ssl ). - `port` (Number) The port particular service listens on. ### Optional - `address` (String) List of IP/IPv6 prefixes from which the service is accessible. - `certificate` (String) The name of the certificate used by a particular service. Applicable only for services that depend on certificates ( www-ssl, api-ssl ). - `disabled` (Boolean) - `max_sessions` (Number) Maximum number of concurrent connections to a particular service. This option is available in RouterOS starting from version 7.16. - `tls_version` (String) Specifies which TLS versions to allow by a particular service. - `vrf` (String) The VRF table this resource operates on. ### Read-Only - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. - `invalid` (Boolean) - `name` (String) Service name. - `proto` (String) ## Import Import is supported using the following syntax: ```shell # Import with the name of the ip service in case of the example use www-ssl terraform import routeros_ip_service.www_ssl www-ssl #Or you can import a resource using one of its attributes terraform import routeros_ip_service.www_ssl "name=xxx" ``` ================================================ FILE: docs/resources/ip_settings.md ================================================ # routeros_ip_settings (Resource) ## Example Usage ```terraform resource "routeros_ip_settings" "settings" { ipv4_multipath_hash_policy = "l3-inner" } ``` ## Schema ### Optional - `accept_redirects` (Boolean) Whether to accept ICMP redirect messages. Typically should be enabled on the host and disabled on routers. - `accept_source_route` (Boolean) Whether to accept packets with the SRR option. Typically should be enabled on the router. - `allow_fast_path` (Boolean) Allows Fast Path. - `arp_timeout` (String) Sets Linux base_reachable_time (base_reachable_time_ms) on all interfaces that use ARP. The initial validity of the ARP entry is picked from the interval [timeout/2 - 3*timeout/2] (default from 15s to 45s) after the neighbor was found. Can use postfix ms, s, m, h, d for milliseconds, seconds, minutes, hours, or days. if no postfix is set then seconds (s) are used. The parameter means how long a valid ARP record will be considered complete if no one communicates with the specific MAC/IP during this time. The parameter does not represent a time when an ARP entry is removed from the ARP cache (see max-neighbor-entries setting). - `icmp_errors_use_inbound_interface_address` (Boolean) If enabled, the ICMP error message reply will be sent with the source address equal to primary address of the receiving interface that caused the error . This feature can be useful for complex network debugging. - `icmp_rate_limit` (Number) Limit the maximum rates for sending ICMP packets whose type matches icmp-rate-mask to specific targets. `0` disables any limiting, other values indicate the minimum space between responses in milliseconds. - `icmp_rate_mask` (String) Mask made of ICMP types for which rates are being limited. - `ip_forward` (Boolean) Enable/disable packet forwarding between interfaces. Resets all configuration parameters to defaults according to RFC1812 for routers. - `ipv4_multipath_hash_policy` (String) IPv4 Hash policy used for ECMP routing in `/ip/settings` menu * l3 -- layer-3 hashing of src IP, dst IP * l3-inner -- layer-3 hashing or inner layer-3 hashing if available * l4 -- layer-4 hashing of src IP, dst IP, IP protocol, src port, dst port - `max_neighbor_entries` (Number) Sets Linux gc_thresh3. A maximum number of allowed neighbors in the ARP table. Since `RouterOS version 7.1`, the default value depends on the installed amount of RAM. It is possible to set a higher value than the default, but it increases the risk of out-of-memory condition. The default values for certain RAM sizes: * 2048 for 64 MB, * 4096 for 128 MB, * 8192 for 256 MB, * 16384 for 512 MB or higher. The ARP cache stores ARP entries, and if some of these entries are incomplete, they can stay in the cache for an indefinite period of time. This will only happen if the number of entries in the cache is less than one-fourth of the maximum number allowed. The reason for this is to prevent the unnecessary running of the garbage-collector when the ARP table is not close to being full. - `route_cache` (Boolean) Disable or enable the Linux route cache. Note that disabling the route cache, will also disable the fast path. - `rp_filter` (String) Disables or enables source validation. * no - No source validation. * strict - Strict mode as defined in RFC3704 Strict Reverse Path. Each incoming packet is tested against the FIB and if the interface is not the best reverse path the packet check will fail. By default failed packets are discarded. * loose - Loose mode as defined in RFC3704 Loose Reverse Path. Each incoming packet's source address is also tested against the FIB and if the source address is not reachable via any interface the packet check will fail. The current recommended practice in RFC3704 is to enable strict mode to prevent IP spoofing from DDoS attacks. If using asymmetric routing or other complicated routing or VRRP, then the loose mode is recommended. `Warning`: strict mode does not work with routing tables - `secure_redirects` (Boolean) Accept ICMP redirect messages only for gateways, listed in the default gateway list. - `send_redirects` (Boolean) Whether to send ICMP redirects. Recommended to be enabled on routers. - `tcp_syncookies` (Boolean) end out syncookies when the syn backlog queue of a socket overflows. This is to prevent the common 'SYN flood attack'. syncookies seriously violate TCP protocol, and disallow the use of TCP extensions, which can result in serious degradation of some services (f.e. SMTP relaying), visible not by you, but to your clients and relays, contacting you. - `tcp_timestamps` (String) Parameter allows to enable/disable TCP timestamps or add random offset to TCP timestamp (default behavior). Disabling timestamps completely may help to reduce spikes of performance drops. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_ip_settings.settings . ``` ================================================ FILE: docs/resources/ip_smb.md ================================================ # routeros_ip_smb (Resource) ## Example Usage ```terraform resource "routeros_ip_smb" "test" { enabled = "auto" domain = "MSHOME" comment = "MikrotikSMB" interfaces = ["all"] } ``` ## Schema ### Optional - `comment` (String) Set comment for the server. - `domain` (String) Name of Windows Workgroup. - `enabled` (String) The default value is 'auto'. This means that the SMB server will automatically be enabled when the first non-disabled SMB share is configured under '/ip smb share'. - `interfaces` (Set of String) List of interfaces on which SMB service will be running. ### Read-Only - `id` (String) The ID of this resource. - `status` (String) ## Import Import is supported using the following syntax: ```shell terraform import routeros_ip_smb.test . ``` ================================================ FILE: docs/resources/ip_ssh_server.md ================================================ # routeros_ip_ssh_server (Resource) ## Example Usage ```terraform resource "routeros_ip_ssh_server" "test" { strong_crypto = true forwarding_enabled = "local" host_key_size = 4096 } ``` ## Schema ### Optional - `allow_none_crypto` (Boolean) Whether to allow connection if cryptographic algorithms are set to none. - `always_allow_password_login` (Boolean) Whether to allow password login at the same time when public key authorization is configured for a user. - `ciphers` (String) Allow to configure SSH ciphers. - `forwarding_enabled` (String) Allows to control which SSH forwarding method to allow: * no - SSH forwarding is disabled; * local - Allow SSH clients to originate connections from the server(router), this setting controls also dynamic forwarding; * remote - Allow SSH clients to listen on the server(router) and forward incoming connections; * both - Allow both local and remote forwarding methods. - `host_key_size` (Number) RSA key size when host key is being regenerated. - `host_key_type` (String) Select host key type. - `publickey_authentication_options` (String) Sets public key authentication options. The touch-required option causes public key authentication using a FIDO authenticator algorithm to always require the signature to attest that a physically present user explicitlyconfirmed the authentication (usually by touching the authenticator). The verify-required option requires a FIDO key signature attest that the user was verified, e.g. via a PIN. - `strong_crypto` (Boolean) Use stronger encryption. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_ip_ssh_server.test . ``` ================================================ FILE: docs/resources/ip_tftp.md ================================================ # routeros_ip_tftp (Resource) ## Example Usage ```terraform resource "routeros_ip_tftp" "file" { ip_addresses = ["10.0.0.0/24"] req_filename = "file.txt" real_filename = routeros_file.file.name read_only = true } ``` ## Schema ### Optional - `allow` (Boolean) Allow connection. - `allow_overwrite` (Boolean) If `true`, overwriting the file is allowed. - `allow_rollover` (Boolean) If set, server will allow sequence number to roll over when maximum value is reached. This is used to enable large downloads using TFTP server. - `disabled` (Boolean) - `ip_addresses` (Set of String) Range of IP addresses accepted as clients. If empty `0.0.0.0/0` will be used. - `read_only` (Boolean) Sets if file can be written to. If set to `false` write attempts will fail with an error. - `reading_window_size` (String) TFTP Windowsize option value. - `real_filename` (String) If `req-filename` and `real-filename` values are set and valid, the requested filename will be replaced with matched file. This field has to be set. If multiple regex are specified in `req-filename`, with this field you can set which ones should match, so this rule is validated. `real-filename` format for using multiple regex is `filename\0\5\6`. - `req_filename` (String) Requested filename as regular expression (regex) if field is left empty it defaults to `.*`. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/tftp get [print show-ids]] terraform import routeros_ip_tftp.file "*0" ``` ================================================ FILE: docs/resources/ip_tftp_settings.md ================================================ # routeros_ip_tftp_settings (Resource) ## Example Usage ```terraform resource "routeros_ip_tftp_settings" "tftp_settings" { max_block_size = 4096 } ``` ## Schema ### Optional - `max_block_size` (Number) Maximum accepted block size value. During transfer negotiation phase, RouterOS device will not negotiate larger value than this. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_ip_tftp_settings.tftp_settings . ``` ================================================ FILE: docs/resources/ip_traffic_flow.md ================================================ # routeros_ip_traffic_flow (Resource) ## Example Usage ```terraform resource "routeros_ip_traffic_flow" "test" { packet_sampling = true sampling_interval = 2222 sampling_space = 1111 } ``` ## Schema ### Optional - `active_flow_timeout` (String) Maximum life-time of a flow. - `cache_entries` (String) Number of flows which can be in router's memory simultaneously. - `inactive_flow_timeout` (String) How long to keep the flow active, if it is idle. If a connection does not see any packet within this timeout, then traffic-flow will send a packet out as a new flow. If this timeout is too small it can create a significant amount of flows and overflow the buffer. - `interfaces` (String) Names of those interfaces will be used to gather statistics for traffic-flow. To specify more than one interface, separate them with a comma. - `packet_sampling` (Boolean) Enable or disable packet sampling feature. - `sampling_interval` (Number) The number of packets that are consecutively sampled. - `sampling_space` (Number) The number of packets that are consecutively omitted. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_ip_traffic_flow.test . ``` ================================================ FILE: docs/resources/ip_traffic_flow_ipfix.md ================================================ # routeros_ip_traffic_flow_ipfix (Resource) ## Example Usage ```terraform resource "routeros_ip_traffic_flow_ipfix" "test" { nat_events = true dst_port = false } ``` ## Schema ### Optional - `bytes` (String) Total number of bytes processed in the flow. - `dst_address` (String) The destination IP address of the flow. - `dst_address_mask` (String) Network mask for the destination address. - `dst_mac_address` (String) Destination MAC address. - `dst_port` (String) Destination port number. - `first_forwarded` (String) Timestamp of the first packet forwarded in a flow. - `gateway` (String) IP address of the gateway through which the flow was routed. - `icmp_code` (String) ICMP code for error messaging and operational information. - `icmp_type` (String) Type of ICMP message, important for diagnostic messages. - `igmp_type` (String) Type of Internet Group Management Protocol operation. - `in_interface` (String) Interface through which packets of the flow are received. - `ip_header_length` (String) Length of the IP header. - `ip_total_lenght` (String) Length of the IP packet in bytes. - `ipv6_flow_label` (String) Label field from an IPv6 header, used to classify flows. - `is_multicast` (String) Indicates whether the flow is a multicast flow. - `last_forwarded` (String) Timestamp of the last packet forwarded in a flow. - `nat_dst_address` (String) Translated destination IP address by NAT. - `nat_dst_port` (String) Translated destination port number by NAT. - `nat_events` (String) Events related to Network Address Translation for the flow. - `nat_src_address` (String) Translated source IP address by NAT. - `nat_src_port` (String) Translated source port number by NAT. - `out_interface` (String) Interface through which packets of the flow are sent out. - `packets` (String) Number of packets processed in the flow. - `protocol` (String) Protocol number (e.g., TCP, UDP, ICMP). - `src_address` (String) The source IP address of the flow. - `src_address_mask` (String) Network mask for the source address, useful in summarizing data. - `src_mac_address` (String) Source MAC address. - `src_port` (String) Source port number. - `sys_init_time` (String) System initialization time, can be used for timing analysis. - `tcp_ack_num` (String) Acknowledgment number in a TCP connection. - `tcp_flags` (String) Flags from the TCP header (e.g., SYN, ACK). - `tcp_seq_num` (String) Sequence number in a TCP connection. - `tcp_window_size` (String) Window size in a TCP connection, indicating the scale of received data buffering. - `tos` (String) Type of Service field in the IP header, indicating priority and handling of the packet. - `ttl` (String) Time To Live for the packet, decremented by each router to prevent infinite loops. - `udp_length` (String) Length of the UDP payload. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_ip_traffic_flow_ipfix.test . ``` ================================================ FILE: docs/resources/ip_traffic_flow_target.md ================================================ # routeros_ip_traffic_flow_target (Resource) ## Example Usage ```terraform resource "routeros_ip_traffic_flow_target" "test" { dst_address = "192.168.0.2" port = 2055 version = "9" } ``` ## Schema ### Optional - `disabled` (Boolean) - `dst_address` (String) IP address of the host which receives Traffic-Flow statistic packets from the router. - `port` (Number) Port (UDP) of the host which receives Traffic-Flow statistic packets from the router. - `src_address` (String) IP address used as source when sending Traffic-Flow statistics. - `v9_template_refresh` (Number) Number of packets after which the template is sent to the receiving host (only for NetFlow version 9 and IPFIX). - `v9_template_timeout` (String) After how long to send the template, if it has not been sent. (only for NetFlow version 9 and IPFIX). - `version` (String) Which version format of NetFlow to use. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/traffic/flow/target get [print show-ids]] terraform import routeros_ip_traffic_flow_target.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_traffic_flow_target.test "dst_address=xxx" ``` ================================================ FILE: docs/resources/ip_upnp.md ================================================ # routeros_ip_upnp (Resource) *If you do not disable the `allow-disable-external-interface`, any user from the local network will be able (without any authentication procedures) to disable the router's external interface.* ## Example Usage ```terraform resource "routeros_ip_upnp" "test" { allow_disable_external_interface = true enabled = true show_dummy_rule = true } ``` ## Schema ### Optional - `allow_disable_external_interface` (Boolean) Whether or not should the users be allowed to disable the router's external interface. This functionality (for users to be able to turn the router's external interface off without any authentication procedure) is required by the standard, but as it is sometimes not expected or unwanted in UPnP deployments which the standard was not designed for (it was designed mostly for home users to establish their own local networks), you can disable this behavior - `enabled` (Boolean) Enable UPnP service. - `show_dummy_rule` (Boolean) Enable a workaround for some broken implementations, which are handling the absence of UPnP rules incorrectly (for example, popping up error messages). This option will instruct the server to install a dummy (meaningless) UPnP rule that can be observed by the clients, which refuse to work correctly otherwise ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_ip_upnp.test . ``` ================================================ FILE: docs/resources/ip_upnp_interfaces.md ================================================ # routeros_ip_upnp_interfaces (Resource) ## Example Usage ```terraform resource "routeros_ip_upnp_interfaces" "test" { interface = "ether1" type = "external" forced_external_ip = "0.0.0.0" } ``` ## Schema ### Required - `interface` (String) Interface name on which UPnP will be running. ### Optional - `disabled` (Boolean) - `forced_ip` (String) Allow specifying what public IP to use if the external interface has more than one IP available. - `type` (String) UPnP interface type: * external - the interface a global IP address is assigned to * internal - router's local interface the clients are connected to ### Read-Only - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/upnp/interfaces get [print show-ids]] terraform import routeros_ip_upnp_interfaces.test '*1' #Or you can import a resource using one of its attributes terraform import routeros_ip_upnp_interfaces.test "name=xxx" ``` ================================================ FILE: docs/resources/ip_vrf.md ================================================ # routeros_ip_vrf (Resource) ## Example Usage ```terraform resource "routeros_interface_veth" "veth1" { name = "veth1" } resource "routeros_interface_veth" "veth2" { name = "veth2" } resource "routeros_ip_vrf" "test_vrf_a" { disabled = true name = "vrf_1" comment = "Custom routing" interfaces = ["veth1", "veth2"] depends_on = [routeros_interface_veth.veth1, routeros_interface_veth.veth2] } ``` ## Schema ### Required - `interfaces` (Set of String) At least one interface must be added to the VRF. - `name` (String) Unique name of the VRF. ### Optional - `comment` (String) - `disabled` (Boolean) ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/vrf get [print show-ids]] terraform import routeros_ip_vrf.test_vrf_a "*0" # or terraform import routeros_ip_vrf.test_vrf_a "vrf_1" # or terraform import routeros_ip_vrf.test_vrf_a `"comment=Custom routing"` ``` ================================================ FILE: docs/resources/ipip.md ================================================ # routeros_ipip (Resource) ## Schema ### Required - `name` (String) Name of the ipip interface. ### Optional - `allow_fast_path` (Boolean) Whether to allow FastPath processing. Must be disabled if IPsec tunneling is used. - `clamp_tcp_mss` (Boolean) Controls whether to change MSS size for received TCP SYN packets. When enabled, a router will change the MSS size for received TCP SYN packets if the current MSS size exceeds the tunnel interface MTU (taking into account the TCP/IP overhead). The received encapsulated packet will still contain the original MSS, and only after decapsulation the MSS is changed. - `comment` (String) - `disabled` (Boolean) - `dont_fragment` (String) - `dscp` (String) Set dscp value in GRE header to a fixed value '0..63' or 'inherit' from dscp value taken from tunnelled traffic. - `ipsec_secret` (String, Sensitive) When secret is specified, router adds dynamic IPsec peer to remote-address with pre-shared key and policy (by default phase2 uses sha1/aes128cbc). - `keepalive` (String) Tunnel keepalive parameter sets the time interval in which the tunnel running flag will remain even if the remote end of tunnel goes down. If configured time,retries fail, interface running flag is removed. Parameters are written in following format: `KeepaliveInterval,KeepaliveRetries` where `KeepaliveInterval` is time interval and `KeepaliveRetries` - number of retry attempts. `KeepaliveInterval` is integer 0..4294967295 - `local_address` (String) Source address of the tunnel packets, local on the router. - `mtu` (String) Layer3 Maximum transmission unit ('auto', 0 .. 65535). Look for the exact minimum value in the MikroTik documentation - `remote_address` (String) IP address of the remote end of the tunnel. ### Read-Only - `actual_mtu` (Number) - `id` (String) The ID of this resource. - `l2mtu` (Number) Layer2 Maximum transmission unit. [See](https://wiki.mikrotik.com/wiki/Maximum_Transmission_Unit_on_RouterBoards). - `running` (Boolean) ================================================ FILE: docs/resources/ipv6_address.md ================================================ # routeros_ipv6_address (Resource) ## Example Usage ```terraform resource "routeros_ipv6_address" "ipv6_address" { address = "fd55::1/64" interface = "ether1" disabled = false } ``` ## Schema ### Required - `interface` (String) Name of the interface. ### Optional - `address` (String) IPv6 address. Using the eui_64 and from_pool options can transform the original address! [See docs](https://wiki.mikrotik.com/wiki/Manual:IPv6/Address#Properties) - `advertise` (Boolean) Whether to enable stateless address configuration. The prefix of that address is automatically advertised to hosts using ICMPv6 protocol. The option is set by default for addresses with prefix length 64. - `auto_link_local` (Boolean) If newly created address is manual link-local address this setting allows to override dynamically created IPv6 link-local address. - `comment` (String) - `disabled` (Boolean) - `eui_64` (Boolean) Whether to calculate EUI-64 address and use it as last 64 bits of the IPv6 address. - `from_pool` (String) Name of the pool from which prefix will be taken to construct IPv6 address taking last part of the address from address property. - `no_dad` (Boolean) If set indicates that address is anycast address and Duplicate Address Detection should not be performed. - `vrf` (String) The VRF table this resource operates on. ### Read-Only - `actual_interface` (String) Name of the actual interface the logical one is bound to. - `deprecated` (Boolean) Whether address is deprecated - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `global` (Boolean) Whether address is global. - `id` (String) The ID of this resource. - `invalid` (Boolean) - `link_local` (Boolean) Whether address is link local. - `slave` (Boolean) Whether address belongs to an interface which is a slave port to some other master interface ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ipv6/address get [print show-ids]] terraform import routeros_ipv6_address.ipv6_address "*0" ``` ================================================ FILE: docs/resources/ipv6_dhcp_client.md ================================================ # routeros_ipv6_dhcp_client (Resource) ## Example Usage ```terraform resource "routeros_ipv6_dhcp_client" "inet_provider" { pool_name = "pub-add-pool" interface = "ether1" add_default_route = true pool_prefix_length = 64 request = ["prefix"] disabled = false } resource "routeros_ipv6_dhcp_client" "client" { pool_name = "pub-add-pool" interface = "ether1" add_default_route = true pool_prefix_length = 64 request = ["prefix"] } ``` ## Schema ### Required - `interface` (String) Name of the interface. - `request` (List of String) To choose if the DHCPv6 request will ask for the address, info or the IPv6 prefix. ### Optional - `accept_prefix_without_address` (Boolean) - `add_default_route` (Boolean) Whether to add default IPv6 route after a client connects. - `allow_reconfigure` (Boolean) Allow reconfigure messages. - `check_gateway` (String) Method on how to check gateway reachability. - `comment` (String) - `custom_iana_id` (String) Allow to specify custom IANA ID. - `custom_iapd_id` (String) Allow to specify custom IAPD ID. - `default_route_distance` (Number) Distance of default route. Applicable if add-default-route is set to yes. - `default_route_tables` (Set of String) List of routing tables to which default route must be added. Table name can be proceeded with ":x" where x would be the distance for the route to be installed with. - `dhcp_options` (Set of String) Options that are sent to the DHCP server. - `disabled` (Boolean) - `pool_name` (String) Name of the IPv6 pool in which received IPv6 prefix will be added - `pool_prefix_length` (Number) Prefix length parameter that will be set for IPv6 pool in which received IPv6 prefix is added. Prefix length must be greater than the length of the received prefix, otherwise, prefix-length will be set to received prefix length + 8 bits. - `prefix_address_lists` (Set of String) Names of the firewall address lists to which received prefix will be added. - `prefix_hint` (String) Include a preferred prefix length. - `rapid_commit` (Boolean) Enable DHCP rapid commit (fast address assignment) - `script` (String) Run this script on the DHCP-client status change. Available variables: * pd-valid - if the prefix is acquired by the client; * pd-prefix - the prefix acquired by the client if any; * na-valid - if the address is acquired by the client; * na-address - the address acquired by the client if any. * options - array of received options (only ROSv7) - `use_interface_duid` (Boolean) Specifies the MAC address of the specified interface as the DHCPv6 client DUID. - `use_peer_dns` (Boolean) Whether to accept the DNS settings advertised by the IPv6 DHCP Server. - `validate_server_duid` (Boolean) Whether to validate the DUID of the IPv6 DHCP Server. ### Read-Only - `address` (String) IPv6 address, which is assigned to DHCPv6 Client from the Server. - `dhcp_server_v6` (String) The IPv6 address of the DHCP server - `duid` (String) Auto-generated DUID that is sent to the server. DUID is generated using one of the MAC addresses available on the router. - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `expires_after` (String) A time when the IPv6 prefix expires (specified by the DHCPv6 server). - `gateway` (String) The IP address of the gateway which is assigned by DHCP server. - `id` (String) The ID of this resource. - `invalid` (Boolean) - `prefix` (String) Shows received IPv6 prefix from DHCPv6-PD server - `status` (String) Shows the status of DHCPv6 Client: * stopped - dhcpv6 client is stopped * searching - sending `solicit` and trying to get `advertise` Shows actual (resolved) gateway and interface that will be used for packet forwarding.requesting - sent `request` waiting for `reply` * bound - received `reply`. Prefix assigned. * renewing - sent `renew`, waiting for `reply` * rebinding - sent `rebind`, waiting for `reply` * error - reply was not received in time or some other error occurred. * stopping - sent `release` ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ipv6/dhcp-client/ get [print show-ids]] terraform import routeros_ipv6_dhcp_client.inet_provider "*1" #Or you can import a resource using one of its attributes terraform import routeros_ipv6_dhcp_client.inet_provider "name=xxx" ``` ================================================ FILE: docs/resources/ipv6_dhcp_client_option.md ================================================ # routeros_ipv6_dhcp_client_option (Resource) ## Example Usage ```terraform resource "routeros_ipv6_dhcp_client_option" "option" { name = "my-dhcp-option" code = 60 } ``` ## Schema ### Required - `code` (Number) The dhcp-client option code. - `name` (String) The name that will be used in dhcp-client. ### Optional - `value` (String) The dhcp-client option ### Read-Only - `id` (String) The ID of this resource. - `raw_value` (String) raw_value is computed from value. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ipv6/dhcp-client/option get [print show-ids]] terraform import routeros_ipv6_dhcp_client_option.option "*0" #Or you can import a resource using one of its attributes terraform import routeros_ipv6_dhcp_client_option.option "name=xxx" ``` ================================================ FILE: docs/resources/ipv6_dhcp_server.md ================================================ # routeros_ipv6_dhcp_server (Resource) ## Example Usage ```terraform resource "routeros_ipv6_pool" "pool-0" { name = "test-pool-0" prefix = "2001:db8:40::/48" prefix_length = 64 } resource "routeros_ipv6_dhcp_server" "test" { address_pool = routeros_ipv6_pool.pool-0.name interface = "bridge" lease_time = "1m" name = "test-dhcpv6" preference = 128 } ``` ## Schema ### Required - `interface` (String) The interface on which server will be running. - `name` (String) Reference name. ### Optional - `address_lists` (Set of String) Firewall address lists to which the allocated addresses and prefixes will be added if the lease is bound. - `address_pool` (String) IPv6 pool, from which to take IPv6 address for the clients. The prefix length of the pool must be 128. - `allow_dual_stack_queue` (Boolean) Creates a single simple queue entry for both IPv4 and IPv6 addresses, and uses the MAC address and DUID for identification. Requires IPv6 DHCP Server to have this option enabled as well to work properly. - `binding_script` (String) A script that will be executed after binding is assigned or de-assigned. Internal `global` variables that can be used in the script: - bindingBound - set to `1` if bound, otherwise set to `0` - bindingServerName - dhcp server name - bindingDUID - DUID - bindingAddress - active address - bindingPrefix - active prefix. - `comment` (String) - `dhcp_option` (Set of String) Add additional DHCP options from option list. - `disabled` (Boolean) - `ignore_ia_na_bindings` (Boolean) Do not reply to DHCPv6 address requests and process only prefixes. Without this setting even if server does not have address-pool configured, it has to respond to client that there is no address available for the client. That can lead up to the situation when DHCPv6 client requests address and prefix in a loop. - `insert_queue_before` (String) Specify where to place dynamic simple queue entries for static DCHP leases with a rate-limit parameter set. - `lease_time` (String) The time that a client may use the assigned address. The client will try to renew this address after half of this time and will request a new address after the time limit expires. - `parent_queue` (String) A dynamically created queue for this lease will be configured as a child queue of the specified parent queue. - `preference` (Number) - `prefix_pool` (String) IPv6 pool, from which to take IPv6 prefix for the clients. - `rapid_commit` (Boolean) - `route_distance` (Number) Distance of the route. - `use_radius` (Boolean) Whether to use RADIUS server. - `use_reconfigure` (Boolean) Allow the server to send Reconfigure messages to clients. ### Read-Only - `duid` (String) DUID value. - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. - `invalid` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ipv6/dhcp/server get [print show-ids]] terraform import routeros_ipv6_dhcp_server.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ipv6_dhcp_server.test "name=test-dhcpv6" ``` ================================================ FILE: docs/resources/ipv6_dhcp_server_option.md ================================================ # routeros_ipv6_dhcp_server_option (Resource) ## Example Usage ```terraform resource "routeros_ipv6_dhcp_server_option" "test" { name = "domain-search" code = 24 value = "0x07'example'0x05'local'0x00" } ``` ## Schema ### Required - `code` (Number) Dhcp option [code](https://www.ipamworldwide.com/ipam/isc-dhcpv6-options.html). - `name` (String) Descriptive name of the option. ### Optional - `comment` (String) - `value` (String) Parameter's value. Available data types for options are: - `'test'` -> ASCII to Hex 0x74657374 - `'10.10.10.10'` -> Unicode IP to Hex 0x0a0a0a0a - `s'10.10.10.10'` -> ASCII to Hex 0x31302e31302e31302e3130 - `s'160'` -> ASCII to Hex 0x313630 - `'10'` -> Decimal to Hex 0x0a - `0x0a0a` -> No conversion - `$(VARIABLE)` -> hardcoded values RouterOS has predefined variables that can be used: - `HOSTNAME` - client hostname - `RADIUS_MT_STR1` - from radius MT attr nr. `24` - `RADIUS_MT_STR2` - from radius MT attr nr. `25` - `REMOTE_ID` - agent remote-id - `NETWORK_GATEWAY - the first gateway from `/ip dhcp-server network`, note that this option won't work if used from lease. Now it is also possible to combine data types into one, for example: `0x01'vards'$(HOSTNAME)`For example if HOSTNAME is 'kvm', then raw value will be 0x0176617264736b766d. ### Read-Only - `id` (String) The ID of this resource. - `raw_value` (String) Read-only field which shows raw DHCP option value (the format actually sent out). ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ipv6/dhcp/server/option get [print show-ids]] terraform import routeros_ipv6_dhcp_server_option.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ipv6_dhcp_server_option.test "name=domain-search" ``` ================================================ FILE: docs/resources/ipv6_dhcp_server_option_sets.md ================================================ # routeros_ipv6_dhcp_server_option_sets (Resource) ## Example Usage ```terraform resource "routeros_ipv6_dhcp_server_option" "domain-search" { name = "domain-search" code = 24 value = "0x07'example'0x05'local'0x00" } resource "routeros_ipv6_dhcp_server_option_sets" "test" { name = "test-set" options = [routeros_ipv6_dhcp_server_option.domain-search.name] } ``` ## Schema ### Required - `name` (String) The name of the DHCPv6 option. ### Optional - `comment` (String) - `options` (Set of String) The list of options. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ipv6/dhcp/server/option/sets get [print show-ids]] terraform import routeros_ipv6_dhcp_server_option_sets.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ipv6_dhcp_server_option_sets.test "name=test-set" ``` ================================================ FILE: docs/resources/ipv6_firewall_addr_list.md ================================================ # routeros_ipv6_firewall_addr_list (Resource) ## Example Usage ```terraform resource "routeros_ipv6_firewall_addr_list" "example_list" { address = "123:dead:beaf::/64" list = "Example List" } ``` ## Schema ### Required - `address` (String) A single IPv6 address or IPv6 CIDR subnet - `list` (String) Name for the address list of the added IPv6 address. ### Optional - `comment` (String) - `disabled` (Boolean) - `timeout` (String) Time after address will be removed from address list. If timeout is not specified, the address will be stored into the address list permanently. > Please plan your work logic based on the fact that after the timeout > the resource has been destroyed outside of a Terraform. ### Read-Only - `creation_time` (String) Rule creation time - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ipv6/firewall/address-list get [print show-ids]] terraform import routeros_ipv6_firewall_addr_list.example_list "*0" #Or you can import a resource using one of its attributes terraform import routeros_ipv6_firewall_addr_list.example_list "name=xxx" ``` ================================================ FILE: docs/resources/ipv6_firewall_filter.md ================================================ # routeros_ipv6_firewall_filter (Resource) ## Example Usage ```terraform resource "routeros_ipv6_firewall_filter" "rule" { action = "accept" chain = "forward" src_address = "2001:DB8:1000::1" dst_address = "2001:DB8:2000::1" dst_port = "443" protocol = "tcp" } ``` ## Schema ### Required - `action` (String) Action to take if a packet is matched by the rule - `chain` (String) Specifies to which chain rule will be added. If the input does not match the name of an already defined chain, a new chain will be created. ### Optional - `address_list_timeout` (String) Time interval after which the address will be removed from the address list specified by address-list parameter. Used in conjunction with add-dst-to-address-list or add-src-to-address-list actions. - `comment` (String) - `connection_bytes` (String) Matches packets only if a given amount of bytes has been transfered through the particular connection. - `connection_limit` (String) Matches connections per address or address block after given value is reached. Should be used together with connection-state=new and/or with tcp-flags=syn because matcher is very resource intensive. - `connection_mark` (String) Matches packets marked via mangle facility with particular connection mark. If no-mark is set, rule will match any unmarked connection. - `connection_rate` (String) Connection Rate is a firewall matcher that allow to capture traffic based on present speed of the connection (0..4294967295). - `connection_state` (String) Interprets the connection tracking analysis data for a particular packet. - `connection_type` (String) Matches packets from related connections based on information from their connection tracking helpers. - `content` (String) Match packets that contain specified text. - `disabled` (Boolean) - `dscp` (Number) Matches DSCP IP header field. - `dst_address` (String) Matches packets which destination is equal to specified IP or falls into specified IP range. - `dst_address_list` (String) Matches destination address of a packet against user-defined address list. - `dst_address_type` (String) Matches destination address type. - `dst_limit` (String) Matches packets until a given rate is exceeded. - `dst_port` (String) List of destination port numbers or port number ranges. - `headers` (String) Extension headers. Look at the Extras tab in the v6 filter rules. - `hop_limit` (String) IPv6 TTL. Look at the Extras tab in the v6 filter rules. - `icmp_options` (String) Matches ICMP type: code fields. - `in_bridge_port` (String) Actual interface the packet has entered the router if the incoming interface is a bridge. Works only if use-ip-firewall is enabled in bridge settings. - `in_bridge_port_list` (String) Set of interfaces defined in interface list. Works the same as in-bridge-port. - `in_interface` (String) Interface the packet has entered the router. - `in_interface_list` (String) Set of interfaces defined in interface list. Works the same as in-interface. - `ingress_priority` (Number) Matches the priority of an ingress packet. Priority may be derived from VLAN, WMM, DSCP, or MPLS EXP bit. - `ipsec_policy` (String) Matches the policy used by IPsec. Value is written in the following format: direction, policy. - `jump_target` (String) Name of the target chain to jump to. Applicable only if action=jump. - `limit` (String) Matches packets up to a limited rate (packet rate or bit rate). A rule using this matcher will match until this limit is reached. Parameters are written in the following format: rate[/time],burst:mode. - `log` (Boolean) Add a message to the system log. - `log_prefix` (String) Adds specified text at the beginning of every log message. Applicable if action=log or log=yes configured. - `nth` (String) Matches every nth packet: nth=2,1 rule will match every first packet of 2, hence, 50% of all the traffic that is matched by the rule - `out_bridge_port` (String) Actual interface the packet is leaving the router if the outgoing interface is a bridge. Works only if use-ip-firewall is enabled in bridge settings. - `out_bridge_port_list` (String) Set of interfaces defined in interface list. Works the same as out-bridge-port. - `out_interface` (String) Interface the packet is leaving the router. - `out_interface_list` (String) Set of interfaces defined in interface list. Works the same as out-interface. - `packet_mark` (String) Matches packets marked via mangle facility with particular packet mark. If no-mark is set, the rule will match any unmarked packet. - `packet_size` (String) Matches packets of specified size or size range in bytes. - `per_connection_classifier` (String) PCC matcher allows dividing traffic into equal streams with the ability to keep packets with a specific set of options in one particular stream. - `place_before` (String) Before which position the rule will be inserted. > Please check the effect of this option, as it does not work as you think! > Best way to use in conjunction with a data source. See [example](../data-sources/ip_firewall.md#example-usage). - `port` (String) Matches if any (source or destination) port matches the specified list of ports or port ranges. Applicable only if protocol is TCP or UDP - `priority` (Number) Matches the packet's priority after a new priority has been set. Priority may be derived from VLAN, WMM, DSCP, MPLS EXP bit, or from the priority that has been set using the set-priority action. - `protocol` (String) Matches particular IP protocol specified by protocol name or number. - `random` (Number) Matches packets randomly with a given probability. - `reject_with` (String) Specifies ICMP error to be sent back if the packet is rejected. Applicable if action=reject. - `routing_mark` (String) Matches packets marked by mangle facility with particular routing mark. - `src_address` (String) Matches packets which source is equal to specified IP or falls into a specified IP range. - `src_address_list` (String) Matches source address of a packet against user-defined address list. - `src_address_type` (String) Matches source address type. - `src_mac_address` (String) Matches source MAC address of the packet. - `src_port` (String) List of source ports and ranges of source ports. Applicable only if a protocol is TCP or UDP. - `tcp_flags` (String) Matches specified TCP flags. - `tcp_mss` (String) Matches TCP MSS value of an IP packet. - `time` (String) Allows to create a filter based on the packets' arrival time and date or, for locally generated packets, departure time and date. - `tls_host` (String) Allows matching HTTPS traffic based on TLS SNI hostname. ### Read-Only - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. - `invalid` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ipv6/firewall/filter get [print show-ids]] terraform import routeros_ipv6_firewall_filter.rule "*0" #Or you can import a resource using one of its attributes terraform import routeros_ipv6_firewall_filter.rule "name=xxx" ``` ================================================ FILE: docs/resources/ipv6_firewall_mangle.md ================================================ # routeros_ipv6_firewall_mangle (Resource) ## Example Usage ```terraform resource "routeros_ipv6_firewall_mangle" "rule" { action = "change-mss" chain = "forward" out_interface = "pppoe-out" protocol = "tcp" tcp_flags = "syn" new_mss = "1130" tcp_mss = "1301-65535" } ``` ## Schema ### Required - `action` (String) Action to take if a packet is matched by the rule. - `chain` (String) Specifies to which chain rule will be added. If the input does not match the name of an already defined chain, a new chain will be created. ### Optional - `address_list` (String) Name of the address list to be used. Applicable if action is add-dst-to-address-list or add-src-to-address-list. - `address_list_timeout` (String) Time interval after which the address will be removed from the address list specified by address-list parameter. Used in conjunction with add-dst-to-address-list or add-src-to-address-list actions. - `comment` (String) - `connection_bytes` (String) Matches packets only if a given amount of bytes has been transfered through the particular connection. - `connection_limit` (String) Matches connections per address or address block after given value is reached. Should be used together with connection-state=new and/or with tcp-flags=syn because matcher is very resource intensive. - `connection_mark` (String) Matches packets marked via mangle facility with particular connection mark. If no-mark is set, rule will match any unmarked connection. - `connection_nat_state` (String) Can match connections that are srcnatted, dstnatted or both. - `connection_rate` (String) Connection Rate is a firewall matcher that allow to capture traffic based on present speed of the connection (0..4294967295). - `connection_state` (String) Interprets the connection tracking analysis data for a particular packet. - `connection_type` (String) Matches packets from related connections based on information from their connection tracking helpers. - `content` (String) Match packets that contain specified text. - `disabled` (Boolean) - `dscp` (Number) Matches DSCP IP header field. - `dst_address` (String) Matches packets which destination is equal to specified IP or falls into specified IP range. - `dst_address_list` (String) Matches destination address of a packet against user-defined address list. - `dst_address_type` (String) Matches destination address type. - `dst_limit` (String) Matches packets until a given rate is exceeded. - `dst_port` (String) List of destination port numbers or port number ranges. - `icmp_options` (String) Matches ICMP type: code fields. - `in_bridge_port` (String) Actual interface the packet has entered the router if the incoming interface is a bridge. Works only if use-ip-firewall is enabled in bridge settings. - `in_bridge_port_list` (String) Set of interfaces defined in interface list. Works the same as in-bridge-port. - `in_interface` (String) Interface the packet has entered the router. - `in_interface_list` (String) Set of interfaces defined in interface list. Works the same as in-interface. - `ingress_priority` (Number) Matches the priority of an ingress packet. Priority may be derived from VLAN, WMM, DSCP, or MPLS EXP bit. - `ipsec_policy` (String) Matches the policy used by IPsec. Value is written in the following format: direction, policy. - `limit` (String) Matches packets up to a limited rate (packet rate or bit rate). A rule using this matcher will match until this limit is reached. Parameters are written in the following format: rate[/time],burst:mode. - `log` (Boolean) Add a message to the system log. - `log_prefix` (String) Adds specified text at the beginning of every log message. Applicable if action=log or log=yes configured. - `new_connection_mark` (String) Sets a new connection-mark value. - `new_dscp` (Number) Sets a new DSCP value for a packet. - `new_mss` (String) Sets a new MSS for a packet. > clamp-to-pmtu feature sets (DF) bit in the IP header to dynamically discover the PMTU of a path. > Host sends all datagrams on that path with the DF bit set until receives ICMP. > Destination Unreachable messages with a code meaning "fragmentation needed and DF set". > Upon receipt of such a message, the source host reduces its assumed PMTU for the path. - `new_packet_mark` (String) Sets a new packet-mark value. - `new_priority` (String) Sets a new priority for a packet. This can be the VLAN, WMM, DSCP or MPLS EXP priority. This property can also be used to set an internal priority. - `new_routing_mark` (String) Sets a new routing-mark value. - `new_ttl` (String) Sets a new TTL for a packet. - `nth` (String) Matches every nth packet: nth=2,1 rule will match every first packet of 2, hence, 50% of all the traffic that is matched by the rule - `out_bridge_port` (String) Actual interface the packet is leaving the router if the outgoing interface is a bridge. Works only if use-ip-firewall is enabled in bridge settings. - `out_bridge_port_list` (String) Set of interfaces defined in interface list. Works the same as out-bridge-port. - `out_interface` (String) Interface the packet is leaving the router. - `out_interface_list` (String) Set of interfaces defined in interface list. Works the same as out-interface. - `packet_mark` (String) Matches packets marked via mangle facility with particular packet mark. If no-mark is set, the rule will match any unmarked packet. - `packet_size` (String) Matches packets of specified size or size range in bytes. - `passthrough` (Boolean) Whether to let the packet to pass further (like action passthrough) into the firewall or not (property only valid some actions). - `per_connection_classifier` (String) PCC matcher allows dividing traffic into equal streams with the ability to keep packets with a specific set of options in one particular stream. - `place_before` (String) Before which position the rule will be inserted. > Please check the effect of this option, as it does not work as you think! > Best way to use in conjunction with a data source. See [example](../data-sources/ip_firewall.md#example-usage). - `port` (String) Matches if any (source or destination) port matches the specified list of ports or port ranges. Applicable only if protocol is TCP or UDP - `priority` (Number) Matches the packet's priority after a new priority has been set. Priority may be derived from VLAN, WMM, DSCP, MPLS EXP bit, or from the priority that has been set using the set-priority action. - `protocol` (String) Matches particular IP protocol specified by protocol name or number. - `random` (Number) Matches packets randomly with a given probability. - `routing_mark` (String) Matches packets marked by mangle facility with particular routing mark. - `src_address` (String) Matches packets which source is equal to specified IP or falls into a specified IP range. - `src_address_list` (String) Matches source address of a packet against user-defined address list. - `src_address_type` (String) Matches source address type. - `src_mac_address` (String) Matches source MAC address of the packet. - `src_port` (String) List of source ports and ranges of source ports. Applicable only if a protocol is TCP or UDP. - `tcp_flags` (String) Matches specified TCP flags. - `tcp_mss` (String) Matches TCP MSS value of an IP packet. - `time` (String) Allows to create a filter based on the packets' arrival time and date or, for locally generated packets, departure time and date. - `tls_host` (String) Allows matching HTTPS traffic based on TLS SNI hostname. - `ttl` (String) Matches packets TTL value. ### Read-Only - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. - `invalid` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ipv6/firewall/mangle get [print show-ids]] terraform import routeros_ipv6_firewall_mangle.rule "*0" #Or you can import a resource using one of its attributes terraform import routeros_ipv6_firewall_mangle.rule "name=xxx" ``` ================================================ FILE: docs/resources/ipv6_firewall_nat.md ================================================ # routeros_ipv6_firewall_nat (Resource) ## Example Usage ```terraform resource "routeros_ipv6_firewall_nat" "rule" { action = "masquerade" chain = "srcnat" out_interface = "ether16" } ``` ## Schema ### Required - `action` (String) Action to take if a packet is matched by the rule - `chain` (String) Specifies to which chain rule will be added. If the input does not match the name of an already defined chain, a new chain will be created. ### Optional - `address_list` (String) Name of the address list to be used. Applicable if action is add-dst-to-address-list or add-src-to-address-list. - `address_list_timeout` (String) Time interval after which the address will be removed from the address list specified by address-list parameter. Used in conjunction with add-dst-to-address-list or add-src-to-address-list actions. - `comment` (String) - `connection_bytes` (String) Matches packets only if a given amount of bytes has been transfered through the particular connection. - `connection_limit` (String) Matches connections per address or address block after given value is reached. Should be used together with connection-state=new and/or with tcp-flags=syn because matcher is very resource intensive. - `connection_mark` (String) Matches packets marked via mangle facility with particular connection mark. If no-mark is set, rule will match any unmarked connection. - `connection_rate` (String) Connection Rate is a firewall matcher that allow to capture traffic based on present speed of the connection (0..4294967295). - `connection_type` (String) Matches packets from related connections based on information from their connection tracking helpers. - `content` (String) Match packets that contain specified text. - `disabled` (Boolean) - `dscp` (Number) Matches DSCP IP header field. - `dst_address` (String) Matches packets which destination is equal to specified IP or falls into specified IP range. - `dst_address_list` (String) Matches destination address of a packet against user-defined address list. - `dst_address_type` (String) Matches destination address type. - `dst_limit` (String) Matches packets until a given rate is exceeded. - `dst_port` (String) List of destination port numbers or port number ranges. - `icmp_options` (String) Matches ICMP type: code fields. - `in_bridge_port` (String) Actual interface the packet has entered the router if the incoming interface is a bridge. Works only if use-ip-firewall is enabled in bridge settings. - `in_bridge_port_list` (String) Set of interfaces defined in interface list. Works the same as in-bridge-port. - `in_interface` (String) Interface the packet has entered the router. - `in_interface_list` (String) Set of interfaces defined in interface list. Works the same as in-interface. - `ingress_priority` (Number) Matches the priority of an ingress packet. Priority may be derived from VLAN, WMM, DSCP, or MPLS EXP bit. - `ipsec_policy` (String) Matches the policy used by IPsec. Value is written in the following format: direction, policy. - `jump_target` (String) Name of the target chain to jump to. Applicable only if action=jump. - `limit` (String) Matches packets up to a limited rate (packet rate or bit rate). A rule using this matcher will match until this limit is reached. Parameters are written in the following format: rate[/time],burst:mode. - `log` (Boolean) Add a message to the system log. - `log_prefix` (String) Adds specified text at the beginning of every log message. Applicable if action=log or log=yes configured. - `nth` (String) Matches every nth packet: nth=2,1 rule will match every first packet of 2, hence, 50% of all the traffic that is matched by the rule - `out_bridge_port` (String) Actual interface the packet is leaving the router if the outgoing interface is a bridge. Works only if use-ip-firewall is enabled in bridge settings. - `out_bridge_port_list` (String) Set of interfaces defined in interface list. Works the same as out-bridge-port. - `out_interface` (String) Interface the packet is leaving the router. - `out_interface_list` (String) Set of interfaces defined in interface list. Works the same as out-interface. - `packet_mark` (String) Matches packets marked via mangle facility with particular packet mark. If no-mark is set, the rule will match any unmarked packet. - `packet_size` (String) Matches packets of specified size or size range in bytes. - `place_before` (String) Before which position the rule will be inserted. > Please check the effect of this option, as it does not work as you think! > Best way to use in conjunction with a data source. See [example](../data-sources/ip_firewall.md#example-usage). - `port` (String) Matches if any (source or destination) port matches the specified list of ports or port ranges. Applicable only if protocol is TCP or UDP - `priority` (Number) Matches the packet's priority after a new priority has been set. Priority may be derived from VLAN, WMM, DSCP, MPLS EXP bit, or from the priority that has been set using the set-priority action. - `protocol` (String) Matches particular IP protocol specified by protocol name or number. - `random` (Number) Matches packets randomly with a given probability. - `randomise_ports` (Boolean) Randomize to which public port connections will be mapped. - `routing_mark` (String) Matches packets marked by mangle facility with particular routing mark. - `src_address` (String) Matches packets which source is equal to specified IP or falls into a specified IP range. - `src_address_list` (String) Matches source address of a packet against user-defined address list. - `src_address_type` (String) Matches source address type. - `src_mac_address` (String) Matches source MAC address of the packet. - `src_port` (String) List of source ports and ranges of source ports. Applicable only if a protocol is TCP or UDP. - `tcp_mss` (String) Matches TCP MSS value of an IP packet. - `time` (String) Allows to create a filter based on the packets' arrival time and date or, for locally generated packets, departure time and date. - `to_address` (String) Replace original address with specified one. Applicable if action is dst-nat, netmap, same, src-nat. - `to_ports` (String) Replace the original port with the specified one. Applicable if action is dst-nat, redirect, masquerade, netmap, same, src-nat. - `ttl` (String) Matches packets TTL value. ### Read-Only - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. - `invalid` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ipv6/firewall/nat get [print show-ids]] terraform import routeros_ipv6_firewall_nat.rule "*0" #Or you can import a resource using one of its attributes terraform import routeros_ipv6_firewall_nat.rule "name=xxx" ``` ================================================ FILE: docs/resources/ipv6_nd_prefix.md ================================================ # routeros_ipv6_nd_prefix (Resource) ## Example Usage ```terraform resource "routeros_ipv6_nd_prefix" "test" { prefix = "fd55::/64" interface = "ether1" preferred_lifetime = "6d24h" } ``` ## Schema ### Required - `interface` (String) Interface name on which stateless auto-configuration will be running. - `prefix` (String) A prefix from which stateless address autoconfiguration generates the valid address. ### Optional - `6to4_interface` (String) If this option is specified, this prefix will be combined with the IPv4 address of the interface name to produce a valid 6to4 prefix. The first 16 bits of this prefix will be replaced by 2002 and the next 32 bits of this prefix will be replaced by the IPv4 address assigned to the interface name at configuration time. The remaining 80 bits of the prefix (including the SLA ID) will be advertised as specified in the configuration file. - `autonomous` (Boolean) When set, indicates that this prefix can be used for autonomous address configuration. Otherwise, prefix information is silently ignored. - `comment` (String) - `disabled` (Boolean) - `on_link` (Boolean) When set, indicates that this prefix can be used for on-link determination. When not set the advertisement makes no statement about the on-link or off-link properties of the prefix. For instance, the prefix might be used for address configuration with some of the addresses belonging to the prefix being on-link and others being off-link. - `preferred_lifetime` (String) Timeframe (relative to the time the packet is sent) after which generated address becomes `deprecated`. Deprecated is used only for already existing connections and is usable until valid lifetime expires. - `valid_lifetime` (String) The length of time (relative to the time the packet is sent) an address remains in the valid state. The valid lifetime must be greater than or equal to the preferred lifetime. ### Read-Only - `id` (String) The ID of this resource. - `invalid` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ipv6/nd/prefix get [print show-ids]] terraform import routeros_ipv6_nd_prefix.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ipv6_nd_prefix.test "prefix=xxx" ``` ================================================ FILE: docs/resources/ipv6_neighbor_discovery.md ================================================ # routeros_ipv6_neighbor_discovery (Resource) ## Example Usage ```terraform resource "routeros_ipv6_neighbor_discovery" "test" { interface = "ether1" hop_limit = 33 advertise_dns = false advertise_mac_address = true disabled = false managed_address_configuration = true mtu = 9000 other_configuration = true pref64_prefixes = [] ra_delay = "3s" ra_interval = "3m20s-10m" ra_lifetime = "30m" ra_preference = "high" reachable_time = "10m" retransmit_interval = "12m" } ``` ## Schema ### Required - `interface` (String) The interface on which to run neighbor discovery.all - run ND on all running interfaces. ### Optional - `advertise_dns` (Boolean) Option to redistribute DNS server information using RADVD. You will need a running client-side software with Router Advertisement DNS support to take advantage of the advertised DNS information. - `advertise_mac_address` (Boolean) When set, the link-layer address of the outgoing interface is included in the RA. - `comment` (String) - `disabled` (Boolean) - `dns` (String) Specify a single IPv6 address or comma separated list of addresses that will be provided to hosts for DNS server configuration. - `dns_servers` (String) Specify a single IPv6 address or list of addresses that will be provided to hosts for DNS server configuration. - `hop_limit` (Number) The default value that should be placed in the Hop Count field of the IP header for outgoing (unicast) IP packets. - `managed_address_configuration` (Boolean) Name of the IPv6 pool in which received IPv6 prefix will be added - `mtu` (Number) The flag indicates whether hosts should use stateful autoconfiguration (DHCPv6) to obtain addresses - `other_configuration` (Boolean) The flag indicates whether hosts should use stateful autoconfiguration to obtain additional information (excluding addresses). - `pref64` (List of String) Specify IPv6 prefix or list of prefixes within /32, /40. /48, /56, /64, or /96 subnet that will be provided to hosts as NAT64 prefixes. - `ra_delay` (String) The minimum time allowed between sending multicast router advertisements from the interface. - `ra_interval` (String) The min-max interval allowed between sending unsolicited multicast router advertisements from the interface. - `ra_lifetime` (String) Specify the router preference that is communicated to IPv6 hosts through router advertisements.The preference value in the router advertisements enables IPv6 hosts to select a default router to reach a remote destination - `ra_preference` (String) Specify the router preference that is communicated to IPv6 hosts through router advertisements.The preference value in the router advertisements enables IPv6 hosts to select a default router to reach a remote destination - `reachable_time` (String) Specify the router preference that is communicated to IPv6 hosts through router advertisements.The preference value in the router advertisements enables IPv6 hosts to select a default router to reach a remote destination - `retransmit_interval` (String) The time between retransmitted Neighbor Solicitation messages.Used by address resolution and the Neighbor Unreachability Detection algorithm (see Sections 7.2 and 7.3 of RFC 2461) ### Read-Only - `default` (Boolean) It's the default item. - `id` (String) The ID of this resource. - `invalid` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ipv6/nd get [print show-ids]] terraform import routeros_ipv6_neighbor_discovery.ndether1 "*0" #Or you can import a resource using one of its attributes terraform import routeros_ipv6_neighbor_discovery.ndether1 "name=xxx" ``` ================================================ FILE: docs/resources/ipv6_pool.md ================================================ # routeros_ipv6_pool (Resource) ## Example Usage ```terraform resource "routeros_ipv6_pool" "test" { name = "test-pool" prefix = "2001:db8:12::/48" prefix_length = 64 } ``` ## Schema ### Required - `name` (String) Descriptive name of the pool. - `prefix_length` (Number) The option represents the prefix size that will be given out to the client. ### Optional - `prefix` (String) Ipv6 address prefix. ### Read-Only - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ipv6/pool get [print show-ids]] terraform import routeros_ipv6_pool.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ipv6_pool.test "name=test-pool" ``` ================================================ FILE: docs/resources/ipv6_route.md ================================================ # routeros_ipv6_route (Resource) ## Example Usage ```terraform resource "routeros_ipv6_route" "a_route" { dst_address = "::/0" gateway = "2001:DB8:1000::1" } ``` ## Schema ### Required - `dst_address` (String) IP prefix of route, specifies destination addresses that this route can be used for. - `gateway` (String) Array of IP addresses or interface names. Specifies which host or interface packets should be sent to (IP | interface | IP%interface | IP@table[, IP | string, [..]]). ### Optional - `blackhole` (Boolean) It's a blackhole route. If you need to cancel route marking, then simply delete the parameter from the configuration of the TF. The value of the parameter (true or false) has no effect on the MT processing logic. - `check_gateway` (String) Currently used check-gateway option. - `comment` (String) - `disabled` (Boolean) - `distance` (Number) Value used in route selection. Routes with smaller distance value are given preference. - `pref_src` (String) Which of the local IP addresses to use for locally originated packets that are sent via this route. Value of this property has no effect on forwarded packets. If value of this property is set to IP address that is not local address of this router then the route will be inactive (in ROS v6, ROS v7 allows IP spoofing). - `routing_table` (String) Routing table this route belongs to. - `scope` (Number) Used in nexthop resolution. Route can resolve nexthop only through routes that have scope less than or equal to the target-scope of this route. - `suppress_hw_offload` (Boolean) - `target_scope` (Number) Used in nexthop resolution. This is the maximum value of scope for a route through which a nexthop of this route can be resolved. - `vrf_interface` (String) VRF interface name. ### Read-Only - `active` (Boolean) A flag indicates whether the route is elected as Active and eligible to be added to the FIB. - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `ecmp` (Boolean) A flag indicates whether the route is added as an Equal-Cost Multi-Path route in the FIB. - `hw_offloaded` (Boolean) Indicates whether the route is eligible to be hardware offloaded on supported hardware. - `id` (String) The ID of this resource. - `immediate_gw` (String) Shows actual (resolved) gateway and interface that will be used for packet forwarding. - `inactive` (Boolean) - `static` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ipv6/route get [print show-ids]] terraform import routeros_ipv6_route.a_route "*0" #Or you can import a resource using one of its attributes terraform import routeros_ipv6_route.a_route "name=xxx" ``` ================================================ FILE: docs/resources/ipv6_settings.md ================================================ # routeros_ipv6_settings (Resource) ## Example Usage ```terraform resource "routeros_ipv6_settings" "settings" { accept_redirects = "no" } ``` ## Schema ### Optional - `accept_redirects` (String) Whether to accept ICMP redirect messages. Typically should be enabled on the host and disabled on routers. - `accept_router_advertisements` (String) Accept router advertisement (RA) messages. If enabled, the router will be able to get the address using stateless address configuration. - `accept_router_advertisements_on` (String) Accept router advertisement (RA) messages. If enabled, the router will be able to get the address using stateless address configuration on specified or specific interfaces - `allow_fast_path` (Boolean) Allows Fast Path. - `disable_ipv6` (Boolean) Enable/disable system wide IPv6 settings (prevents LL address generation). - `disable_link_local_address` (Boolean) Disable automatic link-local address generation for non-VPN interfaces. This can be used when manually configured link-local addresses are being used. - `forward` (Boolean) Enable/disable packet forwarding between interfaces. - `max_neighbor_entries` (Number) A maximum number or IPv6 neighbors. Since RouterOS version 7.1, the default value depends on the installed amount of RAM. It is possible to set a higher value than the default, but it increases the risk of out-of-memory condition. The default values for certain RAM sizes: * 1024 for 64 MB, * 2048 for 128 MB, * 4096 for 256 MB, * 8192 for 512 MB, * 16384 for 1024 MB or higher. - `min_neighbor_entries` (Number) Minimal number of IPv6/Neighbor entries, for which device must allocate memory. - `multipath_hash_policy` (String) IPv6 Hash policy used for ECMP routing in `/ipv6/settings` menu * l3 -- layer-3 hashing of src IP, dst IP, flow label, IP protocol * l3-inner -- layer-3 hashing or inner layer-3 hashing if available * l4 -- layer-4 hashing of src IP, dst IP, IP protocol, src port, dst port. - `soft_max_neighbor_entries` (Number) Expected maximum number of IPv6/Neighbor entries which system should handle. - `stale_neighbor_detect_interval` (String) - `stale_neighbor_timeout` (String) Timeout after which stale IPv6/Neighbor entries should be purged. ### Read-Only - `id` (String) The ID of this resource. - `ipv6_fast_path_active` (Boolean) Indicates whether fast-path is active. - `ipv6_fasttrack_active` (Boolean) Indicates whether fasttrack is active. ## Import Import is supported using the following syntax: ```shell terraform import routeros_ipv6_settings.settings . ``` ================================================ FILE: docs/resources/move_items.md ================================================ # routeros_move_items (Resource) ## Example Usage ```terraform variable "rule" { type = list(object({ chain = string action = string connection_state = optional(string) in_interface_list = optional(string, "all") out_interface_list = optional(string) src_address = optional(string, "0.0.0.0/0") dst_address = optional(string) src_port = optional(string) dst_port = optional(string) protocol = optional(string) comment = optional(string, "(terraform-defined)") log = optional(bool, false) disabled = optional(bool, true) })) default = [ { chain = "input", action = "accept", comment = "00" }, { chain = "input", action = "accept", comment = "01" }, { chain = "input", action = "accept", comment = "02" }, { chain = "input", action = "accept", comment = "03" }, { chain = "input", action = "accept", comment = "04" }, { chain = "input", action = "accept", comment = "05" }, { chain = "input", action = "accept", comment = "06" }, { chain = "input", action = "accept", comment = "07" }, { chain = "input", action = "accept", comment = "08" }, { chain = "input", action = "accept", comment = "09" }, { chain = "input", action = "accept", comment = "10" }, { chain = "input", action = "accept", comment = "11" }, { chain = "input", action = "accept", comment = "12" }, { chain = "input", action = "accept", comment = "13" }, { chain = "input", action = "accept", comment = "14" }, { chain = "input", action = "accept", comment = "15" }, { chain = "input", action = "accept", comment = "16" }, { chain = "input", action = "accept", comment = "17" }, { chain = "input", action = "accept", comment = "18" }, { chain = "input", action = "accept", comment = "19" }, { chain = "input", action = "accept", comment = "20" }, { chain = "input", action = "accept", comment = "21" }, { chain = "input", action = "accept", comment = "22" }, { chain = "input", action = "accept", comment = "23" }, { chain = "input", action = "accept", comment = "24" }, { chain = "input", action = "accept", comment = "25" }, { chain = "input", action = "accept", comment = "26" }, { chain = "input", action = "accept", comment = "27" }, { chain = "input", action = "accept", comment = "28" }, { chain = "input", action = "accept", comment = "29" }, { chain = "input", action = "accept", comment = "30" }, { chain = "input", action = "accept", comment = "31" }, ] } locals { # https://discuss.hashicorp.com/t/does-map-sort-keys/12056/2 # Map keys are always iterated in lexicographical order! rule_map = { for idx, rule in var.rule : format("%03d", idx) => rule } } resource "routeros_ip_firewall_filter" "rules" { for_each = local.rule_map chain = each.value.chain action = each.value.action comment = each.value.comment log = each.value.log disabled = each.value.disabled connection_state = each.value.connection_state in_interface_list = each.value.in_interface_list src_address = each.value.src_address dst_port = each.value.dst_port protocol = each.value.protocol } resource "routeros_move_items" "fw_rules" { # resource_name = "routeros_ip_firewall_filter" resource_path = "/ip/firewall/filter" sequence = [for i, _ in local.rule_map : routeros_ip_firewall_filter.rules[i].id] depends_on = [routeros_ip_firewall_filter.rules] } ``` ## Schema ### Required - `sequence` (List of String) List identifiers in the required sequence. To locate the ```sequence``` before an existing rule, add its ```id``` to the last element of the ```sequence```. ### Optional - `resource_name` (String) Resource name in the notation ```routeros_ip_firewall_filter```. - `resource_path` (String) URL path of the resource in the notation ```/ip/firewall/filter```. ### Read-Only - `id` (String) The ID of this resource. ================================================ FILE: docs/resources/ovpn_server.md ================================================ # routeros_ovpn_server (Resource) ##### *This resource requires a minimum version of RouterOS 7.8!* ## Example Usage ```terraform resource "routeros_ip_pool" "ovpn-pool" { name = "ovpn-pool" ranges = ["192.168.77.2-192.168.77.254"] } resource "routeros_system_certificate" "ovpn_ca" { name = "OpenVPN-Root-CA" common_name = "OpenVPN Root CA" key_size = "prime256v1" key_usage = ["key-cert-sign", "crl-sign"] trusted = true sign { } } resource "routeros_system_certificate" "ovpn_server_crt" { name = "OpenVPN-Server-Certificate" common_name = "Mikrotik OpenVPN" key_size = "prime256v1" key_usage = ["digital-signature", "key-encipherment", "tls-server"] sign { ca = routeros_system_certificate.ovpn_ca.name } } resource "routeros_ppp_profile" "test" { name = "ovpn" local_address = "192.168.77.1" remote_address = "ovpn-pool" use_upnp = "no" } resource "routeros_ppp_secret" "test" { name = "user-test" password = "123" profile = routeros_ppp_profile.test.name } resource "routeros_ovpn_server" "server" { enabled = true certificate = routeros_system_certificate.ovpn_server_crt.name auth = ["sha256", "sha512"] tls_version = "only-1.2" default_profile = routeros_ppp_profile.test.name } # The resource should be created only after the OpenVPN server is enabled! resource "routeros_interface_ovpn_server" "user1" { name = "ovpn-in1" user = "user1" depends_on = [routeros_ovpn_server.server] } ``` ## Schema ### Optional - `auth` (Set of String) Authentication methods that the server will accept. - `certificate` (String) Name of the certificate that the OVPN server will use. - `cipher` (Set of String) Allowed ciphers. - `default_profile` (String) Default profile to use. - `enable_tun_ipv6` (Boolean) Specifies if IPv6 IP tunneling mode should be possible with this OVPN server. - `enabled` (Boolean) Defines whether the OVPN server is enabled or not. - `ipv6_prefix_len` (Number) Length of IPv6 prefix for IPv6 address which will be used when generating OVPN interface on the server side. - `keepalive_timeout` (String) Defines the time period (in seconds) after which the router is starting to send keepalive packets every second. If no traffic and no keepalive responses have come for that period of time (i.e. 2 * keepalive-timeout), not responding client is proclaimed disconnected - `mac_address` (String) Automatically generated MAC address of the server. - `max_mtu` (Number) Maximum Transmission Unit. Max packet size that the OVPN interface will be able to send without packet fragmentation. - `mode` (String) Layer3 or layer2 tunnel mode (alternatively tun, tap) - `netmask` (Number) Subnet mask to be applied to the client. - `port` (Number) Port to run the server on. - `protocol` (String) indicates the protocol to use when connecting with the remote endpoint. - `push_routes` (Set of String) Push routes to the VPN client (available since RouterOS 7.14). - `redirect_gateway` (Set of String) Specifies what kind of routes the OVPN client must add to the routing table. * def1 – Use this flag to override the default gateway by using 0.0.0.0/1 and 128.0.0.0/1 rather than 0.0.0.0/0. This has the benefit of overriding but not wiping out the original default gateway. * disabled - Do not send redirect-gateway flags to the OVPN client. * ipv6 - Redirect IPv6 routing into the tunnel on the client side. This works similarly to the def1 flag, that is, more specific IPv6 routes are added (2000::/4 and 3000::/4), covering the whole IPv6 unicast space. - `reneg_sec` (Number) Renegotiate data channel key after n seconds (default=3600). - `require_client_certificate` (Boolean) If set to yes, then the server checks whether the client's certificate belongs to the same certificate chain. - `tls_version` (String) Specifies which TLS versions to allow. - `tun_server_ipv6` (String) IPv6 prefix address which will be used when generating the OVPN interface on the server side. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_openvpn_server.server . ``` ================================================ FILE: docs/resources/ppp_aaa.md ================================================ # routeros_ppp_aaa (Resource) ## Example Usage ```terraform resource "routeros_ppp_aaa" "settings" { use_radius = true } ``` ## Schema ### Optional - `accounting` (Boolean) An option that enables accounting for users. - `enable_ipv6_accounting` (Boolean) An option that enables IPv6 separate accounting. - `interim_update` (String) Interval between scheduled RADIUS Interim-Update messages. - `use_circuit_id_in_nas_port_id` (Boolean) - `use_radius` (Boolean) An option whether to use RADIUS server. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_ppp_aaa.settings . ``` ================================================ FILE: docs/resources/ppp_profile.md ================================================ # routeros_ppp_profile (Resource) ## Example Usage ```terraform resource "routeros_ppp_profile" "test" { name = "ovpn" local_address = "192.168.77.1" remote_address = "ovpn-pool" use_upnp = "no" } ``` ## Schema ### Required - `name` (String) PPP profile name. ### Optional - `address_list` (String) Address list name to which ppp assigned (on server) or received (on client) address will be added. - `bridge` (String) Name of the bridge interface to which ppp interface will be added as a slave port. Both tunnel endpoints (server and client) must be in bridge in order to make this work, see more details on the BCP bridging manual. - `bridge_horizon` (Number) Used split-horizon value for the dynamically created bridge port. Can be used to prevent bridging loops and isolate traffic. Set the same value for a group of ports, to prevent them from sending data to ports with the same horizon value. - `bridge_learning` (String) Changes MAC learning behavior on the dynamically created bridge port: yes - enables MAC learning no - disables MAC learning default - derive this value from the interface default profile; same as yes if this is the interface default profile. - `bridge_path_cost` (Number) Used path cost for the dynamically created bridge port, used by STP/RSTP to determine the best path, used by MSTP to determine the best path between regions. This property has no effect when a bridge protocol-mode is set to none. - `bridge_port_priority` (Number) Used priority for the dynamically created bridge port, used by STP/RSTP to determine the root port, used by MSTP to determine root port between regions. This property has no effect when a bridge protocol-mode is set to none. - `change_tcp_mss` (String) Modifies connection MSS settings (applies only for IPv4): yes - adjust connection MSS value no - do not adjust connection MSS value default - derive this value from the interface default profile; same as no if this is the interface default profile. - `comment` (String) - `dhcpv6_lease_time` (String) Lease time can be set starting from 7.20ab202, by default time is set to 1d. - `dhcpv6_pd_pool` (String) Name of the IPv6 pool which will be used by dynamically created DHCPv6-PD server when client connects. [Read more >>](https://wiki.mikrotik.com/wiki/Manual:IPv6_PD_over_PPP) - `dhcpv6_use_radius` (Boolean) pecifies value for `use-radius` option selected for dynamically generated DHCPv6 PD servers. - `dns_server` (Set of String) IP address of the DNS server that is supplied to ppp clients. - `idle_timeout` (String) Specifies the amount of time after which the link will be terminated if there are no activity present. Timeout is not set by default. - `incoming_filter` (String) Firewall chain name for incoming packets. Specified chain gets control for each packet coming from the client. The ppp chain should be manually added and rules with action=jump jump-target=ppp should be added to other relevant chains in order for this feature to work. For more information look at the examples section. - `insert_queue_before` (String) Specify where to place dynamic simple queue entries for static DCHP leases with rate-limit parameter set. - `interface_list` (String) Interface list name. - `local_address` (String) Tunnel address or name of the pool from which address is assigned to ppp interface locally. - `on_down` (String) Execute script on user logging off. See on-up for more details. - `on_up` (String) Execute script on user login-event. These are available variables that are accessible for the event script: * user * local-address * remote-address * caller-id * called-id * interface. - `only_one` (String) Defines whether a user is allowed to have more than one ppp session at a time yes - a user is not allowed to have more than one ppp session at a time no - the user is allowed to have more than one ppp session at a time default - derive this value from the interface default profile; same as no if this is the interface default profile. - `outgoing_filter` (String) Firewall chain name for outgoing packets. The specified chain gets control for each packet going to the client. The PPP chain should be manually added and rules with action=jump jump-target=ppp should be added to other relevant chains in order for this feature to work. For more information look at the Examples section. - `parent_queue` (String) Name of parent simple queue. - `queue_type` (String) Queue types. - `rate_limit` (String) Rate limitation in form of rx-rate[/tx-rate] [rx-burst-rate[/tx-burst-rate] [rx-burst-threshold[/tx-burst-threshold] [rx-burst-time[/tx-burst-time] [priority] [rx-rate-min[/tx-rate-min]]]] from the point of view of the router (so 'rx' is client upload, and 'tx' is client download). All rates are measured in bits per second, unless followed by optional 'k' suffix (kilobits per second) or 'M' suffix (megabits per second). If tx-rate is not specified, rx-rate serves as tx-rate too. The same applies for tx-burst-rate, tx-burst-threshold and tx-burst-time. If both rx-burst-threshold and tx-burst-threshold are not specified (but burst-rate is specified), rx-rate and tx-rate are used as burst thresholds. If both rx-burst-time and tx-burst-time are not specified, 1s is used as default. Priority takes values 1..8, where 1 implies the highest priority, but 8 - the lowest. If rx-rate-min and tx-rate-min are not specified rx-rate and tx-rate values are used. The rx-rate-min and tx-rate-min values can not exceed rx-rate and tx-rate values. - `remote_address` (String) Tunnel address or name of the pool from which address is assigned to remote ppp interface. - `remote_ipv6_prefix_pool` (String) Assign prefix from IPv6 pool to the client and install corresponding IPv6 route. - `remote_ipv6_prefix_reuse` (Boolean) If `remote-ipv6-prefix-pool` is specified and includes single `/64`prefix, then prefix can be used only for a single PPP client for RADVD configuration. When this option is set to value `yes`, the same prefix can be reused between all the clients using this PPP profile. - `session_timeout` (String) Maximum time the connection can stay up. By default no time limit is set. - `use_compression` (String) Specifies whether to use data compression or not. yes - enable data compression no - disable data compression default - derive this value from the interface default profile; same as no if this is the interface default profile This setting does not affect OVPN tunnels. - `use_encryption` (String) Specifies whether to use data encryption or not. yes - enable data encryption no - disable data encryption default - derive this value from the interface default profile; same as no if this is the interface default profile require - explicitly requires encryption This setting does not work on OVPN and SSTP tunnels. - `use_ipv6` (String) Specifies whether to allow IPv6. By default is enabled if IPv6 package is installed. yes - enable IPv6 support no - disable IPv6 support default - derive this value from the interface default profile; same as no if this is the interface default profile require - explicitly requires IPv6 support. - `use_mpls` (String) Specifies whether to allow MPLS over PPP. yes - enable MPLS support no - disable MPLS support default - derive this value from the interface default profile; same as no if this is the interface default profile require - explicitly requires MPLS support - `use_upnp` (String) Specifies whether to allow UPnP. - `wins_server` (Set of String) IP address of the WINS server to supply to Windows clients. ### Read-Only - `default` (Boolean) It's the default item. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ppp/profile get [print show-ids]] terraform import routeros_ppp_profile.test *6 #Or you can import a resource using one of its attributes terraform import routeros_ppp_profile.test "name=xxx" ``` ================================================ FILE: docs/resources/ppp_secret.md ================================================ # routeros_ppp_secret (Resource) ## Example Usage ```terraform resource "routeros_ppp_secret" "test" { name = "user-test" password = "123" profile = "default" } ``` ## Schema ### Required - `name` (String) Name used for authentication. ### Optional - `caller_id` (String) For PPTP and L2TP it is the IP address a client must connect from. For PPPoE it is the MAC address (written in CAPITAL letters) a client must connect from. For ISDN it is the caller's number (that may or may not be provided by the operator) the client may dial-in from. - `comment` (String) - `disabled` (Boolean) - `ipv6_routes` (Set of String) IPv6 routes. - `limit_bytes_in` (Number) Maximal amount of bytes for a session that client can upload. - `limit_bytes_out` (Number) Maximal amount of bytes for a session that client can download. - `local_address` (String) IP address that will be set locally on ppp interface. - `password` (String, Sensitive) Password used for authentication. - `profile` (String) Which user profile to use. - `remote_address` (String) IP address that will be assigned to remote ppp interface. - `remote_ipv6_prefix` (String) IPv6 prefix assigned to ppp client. Prefix is added to ND prefix list enabling stateless address auto-configuration on ppp interface.Available starting from v5.0. - `routes` (Set of String) Routes that appear on the server when the client is connected. The route format is: dst-address gateway metric (for example, 10.1.0.0/ 24 10.0.0.1 1). Other syntax is not acceptable since it can be represented in incorrect way. Several routes may be specified separated with commas. This parameter will be ignored for OpenVPN. - `service` (String) Specifies the services that particular user will be able to use. ### Read-Only - `id` (String) The ID of this resource. - `last_caller_id` (String) - `last_disconnect_reason` (String) - `last_logged_out` (String) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ppp/secret get [print show-ids]] terraform import routeros_ppp_secret.test *6 #Or you can import a resource using one of its attributes terraform import routeros_ppp_secret.test "name=xxx" ``` ================================================ FILE: docs/resources/queue_simple.md ================================================ # routeros_queue_simple (Resource) ## Example Usage ```terraform resource "routeros_queue_simple" "test" { name = "server" target = ["10.1.1.1/32"] max_limit = "0/0" } ``` ## Schema ### Required - `name` (String) Queue name. - `target` (Set of String) List of IP address ranges that will be limited by this queue. ### Optional - `bucket_size` (String) - `burst_limit` (String) Maximal upload/download data rate which can be reached while the burst is active. - `burst_threshold` (String) When average data rate is below this value - burst is allowed, as soon as average data rate reach this value - burst is denied (basically this is burst on/off switch). For optimal burst behavior this value should above `limit-at` value and below `max-limit` value - `burst_time` (String) Period of time, in seconds, over which the average upload/download data rate is calculated. This is NOT the time of actual burst. - `comment` (String) - `disabled` (Boolean) - `dst` (String) Allows to select only specific stream (from target address to this destination address) for limitation explain what is target and what is dst and what is upload and what not. - `limit_at` (String) Normal upload/download data rate that is guaranteed to a target. - `max_limit` (String) Maximal upload/download data rate that is allowed for a target to reach to reach what. - `packet_marks` (Set of String) Allows to use marked packets from `/ip firewall mangle`. Take look at this packet flow diagram. You need to make sure that packets are marked before the simple queues (before global-in HTB queue). - `parent` (String) Assigns this queue as a child queue for selected target. Target queue can be HTB queue or any other previously created queue. - `priority` (String) Prioritize one child queue over other child queue. Does not work on parent queues (if queue has at least one child). One is the highest, eight is the lowest priority. Child queue with higher priority will have chance to reach its `max-limit` before child with lower priority. Priority have nothing to do with bursts. - `queue` (String) Choose the type of the queue. - `time` (String) Allow to specify time when particular queue will be active. Router must have correct time settings. - `total_bucket_size` (Number) - `total_burst_limit` (Number) - `total_burst_threshold` (Number) - `total_burst_time` (Number) - `total_limit_at` (Number) - `total_max_limit` (Number) - `total_priority` (Number) - `total_queue` (String) ### Read-Only - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. - `invalid` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/queue/simple get [print show-ids]] terraform import routeros_queue_simple.test *3 #Or you can import a resource using one of its attributes terraform import routeros_queue_simple.test "name=server" ``` ================================================ FILE: docs/resources/queue_tree.md ================================================ # routeros_queue_tree (Resource) ## Example Usage ```terraform resource "routeros_queue_tree" "test" { name = "server" parent = "global" max_limit = "10M" packet_mark = ["pmark-server"] } ``` ## Schema ### Required - `name` (String) Queue tree name. - `parent` (String) Assigns this queue as a child queue for selected target. Target queue can be HTB queue or any other previously created queue. ### Optional - `bucket_size` (String) - `burst_limit` (String) Maximal data rate which can be reached while the burst is active. - `burst_threshold` (String) When average data rate is below this value - burst is allowed, as soon as average data rate reach this value - burst is denied (basically this is burst on/off switch). For optimal burst behavior this value should above `limit-at` value and below `max-limit` value. - `burst_time` (String) Period of time, in seconds, over which the average data rate is calculated. This is NOT the time of actual burst. - `comment` (String) - `disabled` (Boolean) - `limit_at` (String) Normal data rate that is guaranteed to a target. - `max_limit` (String) Maximal data rate that is allowed for a target to reach. - `packet_mark` (Set of String) Allows to use marked packets from `/ip firewall mangle`. Take look at this packet flow diagram. You need to make sure that packets are marked before the simple queues (before global-in HTB queue). - `priority` (Number) Prioritize one child queue over other child queue. Does not work on parent queues (if queue has at least one child). One is the highest, eight is the lowest priority. Child queue with higher priority will have chance to reach its `max-limit` before child with lower priority. Priority have nothing to do with bursts. - `queue` (String) Choose the type of the queue. ### Read-Only - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. - `invalid` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/queue/tree get [print show-ids]] terraform import routeros_queue_tree.test *1000000 #Or you can import a resource using one of its attributes terraform import routeros_queue_tree.test "name=server" ``` ================================================ FILE: docs/resources/queue_type.md ================================================ # routeros_queue_type (Resource) ## Example Usage ```terraform resource "routeros_queue_type" "test" { name = "pcq-test" kind = "pcq" pcq_rate = 0 pcq_limit = 50 pcq_classifier = ["dst-address"] } ``` ## Schema ### Required - `kind` (String) Queue kind. - `name` (String) Type name. ### Optional - `bfifo_limit` (Number) Maximum number of bytes that the BFIFO queue can hold. Applies if `kind` is `bfifo`. - `cake_ack_filter` (String) - `cake_atm` (String) Compensates for ATM cell framing, which is normally found on ADSL links. - `cake_autorate_ingress` (Boolean) Automatic capacity estimation based on traffic arriving at this qdisc. This is most likely to be useful with cellular links, which tend to change quality randomly. The Bandwidth Limit parameter can be used in conjunction to specify an initial estimate. The shaper will periodically be set to a bandwidth slightly below the estimated rate. This estimator cannot estimate the bandwidth of links downstream of itself. - `cake_bandwidth` (Number) Sets the shaper bandwidth. - `cake_diffserv` (String) CAKE can divide traffic into `tins` based on the Diffserv field: * `diffserv4` Provides a general-purpose Diffserv implementation with four tins: Bulk (CS1), 6.25% threshold, generally low priority. Best Effort (general), 100% threshold. Video (AF4x, AF3x, CS3, AF2x, CS2, TOS4, TOS1), 50% threshold. Voice (CS7, CS6, EF, VA, CS5, CS4), 25% threshold. * `diffserv3` (default) Provides a simple, general-purpose Diffserv implementation with three tins: Bulk (CS1), 6.25% threshold, generally low priority. Best Effort (general), 100% threshold. Voice (CS7, CS6, EF, VA, TOS4), 25% threshold, reduced Codel interval. - `cake_flowmode` (String) * `flowblind` - Disables flow isolation; all traffic passes through a single queue for each tin. * `srchost` - Flows are defined only by source address. * `dsthost` Flows are defined only by destination address. * `hosts` - Flows are defined by source-destination host pairs. This is host isolation, rather than flow isolation. * `flows` - Flows are defined by the entire 5-tuple of source address, a destination address, transport protocol, source port, and destination port. This is the type of flow isolation performed by SFQ and fq_codel. * `dual-srchost` Flows are defined by the 5-tuple, and fairness is applied first over source addresses, then over individual flows. Good for use on egress traffic from a LAN to the internet, where it'll prevent any LAN host from monopolizing the uplink, regardless of the number of flows they use. * `dual-dsthost` Flows are defined by the 5-tuple, and fairness is applied first over destination addresses, then over individual flows. Good for use on ingress traffic to a LAN from the internet, where it'll prevent any LAN host from monopolizing the downlink, regardless of the number of flows they use. * `triple-isolate` - Flows are defined by the 5-tuple, and fairness is applied over source *and* destination addresses intelligently (ie. not merely by host-pairs), and also over individual flows. * `nat` Instructs Cake to perform a NAT lookup before applying flow- isolation rules, to determine the true addresses and port numbers of the packet, to improve fairness between hosts `inside` the NAT. This has no practical effect in `flowblind` or `flows` modes, or if NAT is performed on a different host. * `nonat` (default) The cake will not perform a NAT lookup. Flow isolation will be performed using the addresses and port numbers directly visible to the interface Cake is attached to. - `cake_memlimit` (Number) Limit the memory consumed by Cake to LIMIT bytes. By default, the limit is calculated based on the bandwidth and RTT settings. - `cake_mpu` (Number) Rounds each packet (including overhead) up to a minimum length BYTES. - `cake_nat` (Boolean) Instructs Cake to perform a NAT lookup before applying a flow-isolation rule. - `cake_overhead` (Number) Adds BYTES to the size of each packet. BYTES may be negative. - `cake_overhead_scheme` (String) - `cake_rtt` (String) Manually specify an RTT. Default 100ms is suitable for most Internet traffic. - `cake_rtt_scheme` (String) * `datacentre` - For extremely high-performance 10GigE+ networks only. Equivalent to `RTT 100us`. * `lan` - For pure Ethernet (not Wi-Fi) networks, at home or in the office. Don't use this when shaping for an Internet access link. Equivalent to `RTT 1ms`. * `metro` - For traffic mostly within a single city. Equivalent to `RTT 10ms`. regional For traffic mostly within a European-sized country. Equivalent to `RTT 30ms`. * `internet` (default) This is suitable for most Internet traffic. Equivalent to `RTT 100ms`. * `oceanic` - For Internet traffic with generally above-average latency, such as that suffered by Australasian residents. Equivalent to `RTT 300ms`. * `satellite` - For traffic via geostationary satellites. Equivalent to `RTT 1000ms`. * `interplanetary` - So named because Jupiter is about 1 light-hour from Earth. Use this to (almost) completely disable AQM actions. Equivalent to `RTT 3600s`. - `cake_wash` (Boolean) Apply the wash option to clear all extra DiffServ (but not ECN bits), after priority queuing has taken place. - `codel_ce_threshold` (Number) Marks packets above a configured threshold with ECN. - `codel_ecn` (Boolean) An option is used to mark packets instead of dropping them. - `codel_interval` (String) Interval should be set on the order of the worst-case RTT through the bottleneck giving endpoints sufficient time to react. - `codel_limit` (Number) Queue limit, when the limit is reached, incoming packets are dropped. - `codel_target` (String) Represents an acceptable minimum persistent queue delay. - `fq_codel_ce_threshold` (Number) Marks packets above a configured threshold with ECN. - `fq_codel_ecn` (Boolean) An option is used to mark packets instead of dropping them. - `fq_codel_flows` (Number) A number of flows into which the incoming packets are classified. - `fq_codel_interval` (String) Interval should be set on the order of the worst-case RTT through the bottleneck giving endpoints sufficient time to react. - `fq_codel_limit` (Number) Queue limit, when the limit is reached, incoming packets are dropped. - `fq_codel_memlimit` (Number) A total number of bytes that can be queued in this FQ-CoDel instance. Will be enforced from the fq-codel-limit parameter. - `fq_codel_quantum` (Number) A number of bytes used as 'deficit' in the fair queuing algorithm. Default (1514 bytes) corresponds to the Ethernet MTU plus the hardware header length of 14 bytes. - `fq_codel_target` (String) Represents an acceptable minimum persistent queue delay. - `mq_pfifo_limit` (Number) Multi-queue PFIFO limit. - `pcq_burst_rate` (Number) Maximal upload/download data rate which can be reached while the burst for substream is allowed. - `pcq_burst_threshold` (Number) This is value of burst on/off switch. - `pcq_burst_time` (String) Period of time, in seconds, over which the average data rate is calculated. (This is NOT the time of actual burst). - `pcq_classifier` (Set of String) Selection of sub-stream identifiers. - `pcq_dst_address6_mask` (Number) Size of IPV6 network that will be used as dst-address sub-stream identifier. - `pcq_dst_address_mask` (Number) Size of IPv4 network that will be used as dst-address sub-stream identifier. - `pcq_limit` (Number) Queue size of a single sub-stream (in kilobytes). - `pcq_rate` (Number) Maximal available data rate of each sub-steam. - `pcq_src_address6_mask` (Number) Size of IPV6 network that will be used as src-address sub-stream identifier. - `pcq_src_address_mask` (Number) Size of IPv4 network that will be used as src-address sub-stream identifier. - `pcq_total_limit` (Number) Max amount of bytes queued (in kilobytes) for all sub-streams per PCQ instance. Note that each queue tree entry has its own PCQ instance. - `pfifo_limit` (Number) Maximum number of packets that the PFIFO queue can hold. Applies if `kind` is `pfifo`. - `red_avg_packet` (Number) Used by RED for average queue size calculations (for packet to byte translation). - `red_burst` (Number) Number of packets allowed for bursts of packets when there are no packets in the queue. - `red_limit` (Number) RED queue limit in packets. - `red_max_threshold` (Number) The average queue size at which packet marking probability is the highest. - `red_min_threshold` (Number) Average queue size in bytes. - `sfq_allot` (Number) Amount of data in bytes that can be sent in one round-robin round. - `sfq_perturb` (Number) How often hash function must be refreshed. ### Read-Only - `default` (Boolean) It's the default item. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/queue/type get [print show-ids]] terraform import routeros_queue_type.test *3 #Or you can import a resource using one of its attributes terraform import routeros_queue_type.test "name=pcq-test" ``` ================================================ FILE: docs/resources/radius.md ================================================ # routeros_radius (Resource) ## Example Usage ```terraform resource "routeros_radius" "user_manager" { address = "127.0.0.1" service = ["ppp", "login"] } ``` ## Schema ### Required - `address` (String) IPv4 or IPv6 address of RADIUS server. ### Optional - `accounting_backup` (Boolean) An option whether the configuration is for the backup RADIUS server. - `accounting_port` (Number) RADIUS server port used for accounting. - `authentication_port` (Number) RADIUS server port used for authentication. - `called_id` (String) RADIUS calling station identifier. - `certificate` (String) Certificate to use for communication with RADIUS Server with RadSec enabled. - `comment` (String) - `disabled` (Boolean) - `domain` (String) Microsoft Windows domain of client passed to RADIUS servers that require domain validation. - `protocol` (String) An option specifies the protocol to use when communicating with the RADIUS Server. - `radsec_timeout` (String) Timeout after which the request should be resent over RadSec protocol. - `realm` (String) Explicitly stated realm (user domain), so the users do not have to provide proper ISP domain name in the user name. - `require_message_auth` (String) An option whether to require `Message-Authenticator` in received Access-Accept/Challenge/Reject messages. - `secret` (String, Sensitive) The shared secret to access the RADIUS server. - `service` (Set of String) A set of router services that will use the RADIUS server. Possible values: `hotspot`, `login`, `ppp`, `wireless`, `dhcp`, `ipsec`, `dot1x`. - `src_address` (String) Source IPv4/IPv6 address of the packets sent to the RADIUS server. - `timeout` (String) A timeout, after which the request should be resent. ### Read-Only - `id` (String) The ID of this resource. - `status` (String) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/radius get [print show-ids]] terraform import routeros_radius.user_manager *1 #Or you can import a resource using one of its attributes terraform import routeros_radius.user_manager "name=xxx" ``` ================================================ FILE: docs/resources/radius_incoming.md ================================================ # routeros_radius_incoming (Resource) ## Example Usage ```terraform resource "routeros_radius_incoming" "settings" { accept = true } ``` ## Schema ### Optional - `accept` (Boolean) An option whether to accept the unsolicited messages. - `port` (Number) The port number to listen for the requests on. - `vrf` (String) VRF on which service is listening for incoming connections. This option is available in RouterOS starting from version 7.4. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_radius_incoming.settings . ``` ================================================ FILE: docs/resources/routing_bfd_configuration.md ================================================ # routeros_routing_bfd_configuration (Resource) ## Example Usage ```terraform resource "routeros_routing_bfd_configuration" "test" { interfaces = ["lo", "ether2"] vrf = "main" forbid_bfd = true } ``` ## Schema ### Optional - `address_list` (String) Name of the address list in which users IP address will be added. - `addresses` (Set of String) Set of IP (v4 or v6) addresses or CIDR networks. - `disabled` (Boolean) - `forbid_bfd` (Boolean) - `interfaces` (Set of String) List of interfaces. - `min_rx` (String) - `min_tx` (String) - `multiplier` (Number) - `vrf` (String) The VRF table this resource operates on. ### Read-Only - `id` (String) The ID of this resource. - `inactive` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/routing/bfd/configuration get [print show-ids]] terraform import routeros_routing_bfd_configuration.test *3 ``` ================================================ FILE: docs/resources/routing_bgp_connection.md ================================================ # routeros_routing_bgp_connection (Resource) > [!WARNING] Using this resource you may happen unexpected behavior, for example, some of the attributes may not be removable after adding them to the TF configuration. Please report this to GitHub and it may be possible to fix it. Use the resource at your own risk as it is! ## Example Usage ```terraform resource "routeros_routing_bgp_connection" "test" { name = "neighbor-test" as = "65550/5" as_override = true add_path_out = "none" remote { address = "172.17.0.1" as = "12345/5" } local { role = "ebgp" } } ``` ## Schema ### Required - `as` (String) 32-bit BGP autonomous system number. Value can be entered in AS-Plain and AS-Dot formats. The parameter is also used to set up the BGP confederation, in the following format: confederation_as/as . For example, if your AS is 34 and your confederation AS is 43, then as configuration should be as =43/34. - `name` (String) Name of the BGP connection. ### Optional - `add_path_out` (String) - `address_families` (String) List of address families about which this peer will exchange routing information. The remote peer must support (they usually do) BGP capabilities optional parameter to negotiate any other families than IP. - `cisco_vpls_nlri_len_fmt` (String) VPLS NLRI length format type. Used for compatibility with Cisco VPLS. - `cluster_id` (String, Deprecated) In case this instance is a route reflector: the cluster ID of the router reflector cluster to this instance belongs. This attribute helps to recognize routing updates that come from another route reflector in this cluster and avoid routing information looping. Note that normally there is only one route reflector in a cluster; in this case, 'cluster-id' does not need to be configured and BGP router ID is used instead. - `comment` (String) - `connect` (Boolean) Whether to allow the router to initiate the connection. - `disabled` (Boolean) - `hold_time` (String) Specifies the BGP Hold Time value to use when negotiating with peers. According to the BGP specification, if the router does not receive successive KEEPALIVE and/or UPDATE and/or NOTIFICATION messages within the period specified in the Hold Time field of the OPEN message, then the BGP connection to the peer will be closed. The minimal hold-time value of both peers will be actually used (note that the special value 0 or 'infinity' is lower than any other value) infinity - never expire the connection and never send keepalive messages. - `input` (Block List, Max: 1) A group of parameters associated with BGP input. (see [below for nested schema](#nestedblock--input)) - `instance` (String) Name of the instance this VPN is assigned to. - `keepalive_time` (String) How long to keep the BGP session open after the last received 'keepalive' message. - `listen` (Boolean) Whether to listen for incoming connections. - `local` (Block List, Max: 1) A group of parameters associated with BGP input. (see [below for nested schema](#nestedblock--local)) - `multihop` (Boolean) Specifies whether the remote peer is more than one hop away. This option affects outgoing next-hop selection as described in RFC 4271 (for EBGP only, excluding EBGP peers local to the confederation). It also affects: whether to accept connections from peers that are not in the same network (the remote address of the connection is used for this check); whether to accept incoming routes with NEXT_HOP attribute that is not in the same network as the address used to establish the connection; the target-scope of the routes installed from this peer; routes from multi-hop or IBGP peers resolve their next-hops through IGP routes by default. - `nexthop_choice` (String) Affects the outgoing NEXT_HOP attribute selection. Note that next-hops set in filters always take precedence. Also note that the next-hop is not changed on route reflection, except when it's set in the filter. default - select the next-hop as described in RFC 4271 force-self - always use a local address of the interface that is used to connect to the peer as the next-hop; propagate - try to propagate further the next-hop received; i.e. if the route has BGP NEXT_HOP attribute, then use it as the next-hop, otherwise, fall back to the default case. - `output` (Block List, Max: 1) A group of parameters associated with BGP output. (see [below for nested schema](#nestedblock--output)) - `remote` (Block List, Max: 1) A group of parameters associated with BGP input. (see [below for nested schema](#nestedblock--remote)) - `router_id` (String, Deprecated) BGP Router ID to be used. Use the ID from the /routing/router-id configuration by specifying the reference name, or set the ID directly by specifying IP. Equal router-ids are also used to group peers into one instance. - `routing_table` (String) Name of the routing table, to install routes in. - `save_to` (String) Filename to be used to save BGP protocol-specific packet content (Exported PDU) into pcap file. This method allows much simpler peer-specific packet capturing for debugging purposes. Pcap files in this format can also be loaded to create virtual BGP peers to recreate conditions that happened at the time when packet capture was running. - `tcp_md5_key` (String, Sensitive) The key used to authenticate the connection with TCP MD5 signature as described in RFC 2385. If not specified, authentication is not used. - `templates` (Set of String) List of the template names, to inherit parameters from. Useful for dynamic BGP peers. - `use_bfd` (Boolean) Whether to use the BFD protocol for faster connection state detection. - `vrf` (String) The VRF table this resource operates on. ### Read-Only - `id` (String) The ID of this resource. - `inactive` (Boolean) ### Nested Schema for `input` Optional: - `accept_communities` (String) A quick way to filter incoming updates with specific communities. It allows filtering incoming messages directly before they are even parsed and stored in memory, that way significantly reducing memory usage. Regular input filter chain can only reject prefixes which means that it will still eat memory and will be visible in /routing route table as 'not active, filtered'. Changes to be applied required session refresh. - `accept_ext_communities` (String) A quick way to filter incoming updates with specific extended communities. It allows filtering incoming messages directly before they are even parsed and stored in memory, that way significantly reducing memory usage. Regular input filter chain can only reject prefixes which means that it will still eat memory and will be visible in /routing route table as 'not active, filtered'. Changes to be applied required session refresh. - `accept_large_communities` (String) A quick way to filter incoming updates with specific large communities. It allows filtering incoming messages directly before they are even parsed and stored in memory, that way significantly reducing memory usage. Regular input filter chain can only reject prefixes which means that it will still eat memory and will be visible in /routing route table as 'not active, filtered'. Changes to be applied required session refresh. - `accept_nlri` (String) Name of the ipv4/6 address-list. A quick way to filter incoming updates with specific NLRIs. It allows filtering incoming messages directly before they are even parsed and stored in memory, that way significantly reducing memory usage. Regular input filter chain can only reject prefixes which means that it will still eat memory and will be visible in /routing route table as 'not active, filtered'. Changes to be applied required session restart. - `accept_unknown` (String, Deprecated) A quick way to filter incoming updates with specific 'unknown' attributes. It allows filtering incoming messages directly before they are even parsed and stored in memory, that way significantly reducing memory usage. Regular input filter chain can only reject prefixes which means that it will still eat memory and will be visible in /routing route table as 'not active, filtered'. Changes to be applied required session refresh. - `affinity` (String) Configure input multi-core processing. Read more in Routing Protocol Multi-core Support article. alone - input and output of each session are processed in its own process, most likely the best option when there are a lot of cores and a lot of peers afi, instance, vrf, remote-as - try to run input/output of new session in process with similar parameters main - run input/output in the main process (could potentially increase performance on single-core even possibly on multi-core devices with a small amount of cores) input - run output in the same process as input (can be set only for output affinity) - `allow_as` (Number) Indicates how many times to allow your own AS number in AS-PATH, before discarding a prefix. - `filter` (String) Name of the routing filter chain to be used on input prefixes. This happens after NLRIs are processed. If the chain is not specified, then BGP by default accepts everything. - `filter_communities` (String) A quick way to filter incoming updates with specific communities. It allows filtering incoming messages directly before they are even parsed and stored in memory, that way significantly reducing memory usage. Regular input filter chain can only reject prefixes which means that it will still eat memory and will be visible in /routing route table as "not active, filtered". Changes to be applied required session refresh. - `filter_ext_communities` (String) A quick way to filter incoming updates with specific extended communities. It allows filtering incoming messages directly before they are even parsed and stored in memory, that way significantly reducing memory usage. Regular input filter chain can only reject prefixes which means that it will still eat memory and will be visible in /routing route table as "not active, filtered". Changes to be applied required session refresh. - `filter_large_communities` (String) A quick way to filter incoming updates with specific large communities. It allows filtering incoming messages directly before they are even parsed and stored in memory, that way significantly reducing memory usage. Regular input filter chain can only reject prefixes which means that it will still eat memory and will be visible in /routing route table as "not active, filtered". Changes to be applied required session refresh. - `filter_nlri` (String) Name of the filter chain that will filter incoming IPv4/IPv6 NLRIs directly before they are stored in memory, that way significantly reducing memory usage. Regular input filter chain can only reject prefixes which means that it will still eat memory and will be visible in `/routing route` table as `not active, filtered`. Changes to be applied required session restart. - `filter_unknown` (String) A quick way to filter incoming updates with specific "unknown" attributes. It allows filtering incoming messages directly before they are even parsed and stored in memory, that way significantly reducing memory usage. Regular input filter chain can only reject prefixes which means that it will still eat memory and will be visible in /routing route table as "not active, filtered". Changes to be applied required session refresh. - `ignore_as_path_len` (Boolean, Deprecated) Whether to ignore the AS_PATH attribute in the BGP route selection algorithm - `limit_process_routes_ipv4` (Number) Try to limit the amount of received IPv4 routes to the specified number. This number does not represent the exact number of routes going to be installed in the routing table by the peer. BGP session 'clear' command must be used to reset the flag if the limit is reached. - `limit_process_routes_ipv6` (Number) Try to limit the amount of received IPv6 routes to the specified number. This number does not represent the exact number of routes going to be installed in the routing table by the peer. BGP session 'clear' command must be used to reset the flag if the limit is reached. ### Nested Schema for `local` Required: - `role` (String) BGP role, in most common scenarios it should be set to iBGP or eBGP. More information on BGP roles can be found in the corresponding [RFC draft](https://datatracker.ietf.org/doc/draft-ietf-idr-bgp-open-policy/?include_text=1) Optional: - `address` (String) Local connection IPv4/6 address. - `port` (Number) Local connection port. - `ttl` (Number) Time To Live (hop limit) that will be recorded in sent TCP packets. Read-Only: - `default_address` (String) ### Nested Schema for `output` Optional: - `affinity` (String) Configure output multicore processing. Read more in Routing Protocol Multi-core Support article. alone - input and output of each session is processed in its own process, the most likely best option when there are a lot of cores and a lot of peers afi, instance, vrf, remote-as - try to run input/output of new session in process with similar parameters main - run input/output in the main process (could potentially increase performance on single-core even possibly on multicore devices with small amount of cores) input - run output in the same process as input (can be set only for output affinity). - `as_override` (Boolean) If set, then all instances of the remote peer's AS number in the BGP AS-PATH attribute are replaced with the local AS number before sending a route update to that peer. Happens before routing filters and prepending. - `default_originate` (String) Specifies default route (0.0.0.0/0) distribution method. - `default_prepend` (Number) The count of AS prepended to the AS path. - `filter_chain` (String) Name of the routing filter chain to be used on the output prefixes. If the chain is not specified, then BGP by default accepts everything. - `filter_select` (String) Name of the routing select chain to be used for prefix selection. If not specified, then default selection is used. - `keep_sent_attributes` (Boolean) Store in memory sent prefix attributes, required for ' dump-saved-advertisements ' command to work. By default, sent-out prefixes are not stored to preserve the router's memory. An option should be enabled only for debugging purposes when necessary to see currently advertised prefixes. - `network` (String) Name of the address list used to send local networks. The network is sent only if a matching IGP route exists in the routing table. - `no_client_to_client_reflection` (Boolean) Disable client-to-client route reflection in Route Reflector setups. - `no_early_cut` (Boolean) The early cut is the mechanism, to guess (based on default RFC behavior) what would happen with the sent NPLRI when received by the remote peer. If the algorithm determines that the NLRI is going to be dropped, a peer will not even try to send it. However such behavior may not be desired in specific scenarios, then then this option should be used to disable the early cut feature. - `redistribute` (String) Enable redistribution of specified route types. - `remove_private_as` (Boolean) If set, then the BGP AS-PATH attribute is removed before sending out route updates if the attribute contains only private AS numbers. The removal process happens before routing filters are applied and before the local, AS number is prepended to the AS path. ### Nested Schema for `remote` Optional: - `address` (String) Remote IPv4/6 address used to connect and/or listen to. - `allowed_as` (String) List of remote AS numbers that are allowed to connect. Useful for dynamic peer configuration. - `as` (String) Remote AS number. If not specified BGP will determine remote AS automatically from the OPEN message. - `port` (Number) Local connection port. - `ttl` (Number) Acceptable minimum Time To Live, the hop limit for this TCP connection. For example, if 'ttl=255' then only single-hop neighbors will be able to establish the connection. This property only affects EBGP peers. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/routing/bgp/connection get [print show-ids]] terraform import routeros_routing_bgp_connection.test *3 ``` ================================================ FILE: docs/resources/routing_bgp_evpn.md ================================================ # routeros_routing_bgp_evpn (Resource) ## Example Usage ```terraform resource "routeros_routing_bgp_instance" "test" { as = "65000" name = "bgp-instance-1" } resource "routeros_routing_bgp_evpn" "test" { disabled = false export { route_targets = ["1010:1010"] } import { route_targets = ["1010:1010"] } instance = resource.routeros_routing_bgp_instance.test.name name = "bgp-evpn-1" vni = 1010 } ``` ## Schema ### Required - `instance` (String) BGP instance this EVPN is assigned to. - `name` (String) Name of the entry. ### Optional - `comment` (String) - `disabled` (Boolean) - `export` (Block List, Max: 1) A group of parameters associated with the route export. (see [below for nested schema](#nestedblock--export)) - `import` (Block List, Max: 1) A group of parameters associated with the route import. (see [below for nested schema](#nestedblock--import)) - `rd` (String) Specifies the value that gets attached to route so that receiving routers can distinguish advertisements that may otherwise look the same. Used to distinguish between tenants using overlapping IP ranges. Also can be used to simplify convergence and redundancy within Virtual Network. RDs form MLAG pairs should be unique, too. - `vni` (Number) Range of Virtual Network Identifiers. - `vrf` (String) VRF name. ### Read-Only - `id` (String) The ID of this resource. ### Nested Schema for `export` Optional: - `route_targets` (Set of String) List of route targets that will be added to EVPN routes when exporting. ### Nested Schema for `import` Optional: - `route_targets` (Set of String) List of route targets that will be used to import EVPN routes. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/routing/bgp/evpn get [print show-ids]] terraform import routeros_routing_bgp_evpn.test *3 #Or you can import a resource using one of its attributes terraform import routeros_routing_bgp_evpn.test "name=xxx" ``` ================================================ FILE: docs/resources/routing_bgp_instance.md ================================================ # routeros_routing_bgp_instance (Resource) ## Example Usage ```terraform resource "routeros_routing_bgp_instance" "test" { } ``` ## Schema ### Required - `name` (String) Instance name. ### Optional - `as` (String) 32-bit BGP autonomous system number. Value can be entered in AS-Plain and AS-Dot formats. The parameter is also used to set up the BGP confederation, in the following format: confederation_as/as. For example, if your AS is 34 and your confederation AS is 43, then as configuration should be as=43/34. - `cluster_id` (String) In case this instance is a route reflector: the cluster ID of the router reflector cluster to this instance belongs. This attribute helps to recognize routing updates that come from another route reflector in this cluster and avoid routing information looping. Note that normally there is only one route reflector in a cluster; in this case, `cluster-id` does not need to be configured and BGP router ID is used instead. - `comment` (String) - `disabled` (Boolean) - `ignore_as_path_len` (Boolean) Whether to ignore the `AS_PATH` attribute in the BGP route selection algorithm. Works on input. - `router_id` (String) BGP Router ID to be used. Use the ID from the `/routing/router-id` configuration by specifying the reference name, or set the ID directly by specifying IP.Equal router-ids are also used to group peers into one instance. - `routing_table` (String) Name of the routing table, to install routes in. - `vrf` (String) Name of the VRF BGP connections operates on. By default always use the `main` routing table. ### Read-Only - `id` (String) The ID of this resource. - `inactive` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/routing/bgp/instance get [print show-ids]] terraform import routeros_routing_bgp_instance.test *3 #Or you can import a resource using one of its attributes terraform import routeros_routing_bgp_instance.test "name=xxx" ``` ================================================ FILE: docs/resources/routing_bgp_template.md ================================================ # routeros_routing_bgp_template (Resource) > [!WARNING] Using this resource you may happen unexpected behavior, for example, some of the attributes may not be removable after adding them to the TF configuration. Please report this to GitHub and it may be possible to fix it. Use the resource at your own risk as it is! ## Example Usage ```terraform resource "routeros_routing_bgp_template" "test" { name = "test-template" as = 65521 input { limit_process_routes_ipv4 = 5 limit_process_routes_ipv6 = 5 } output { affinity = "alone" keep_sent_attributes = true default_originate = "never" } // save_to = "bgp.dump" } ``` ## Schema ### Required - `as` (String) 32-bit BGP autonomous system number. Value can be entered in AS-Plain and AS-Dot formats. The parameter is also used to set up the BGP confederation, in the following format: confederation_as/as . For example, if your AS is 34 and your confederation AS is 43, then as configuration should be as =43/34. - `name` (String) Name of the BGP template. ### Optional - `add_path_out` (String) - `address_families` (String) List of address families about which this peer will exchange routing information. The remote peer must support (they usually do) BGP capabilities optional parameter to negotiate any other families than IP. - `as_override` (Boolean) If set, then all instances of the remote peer's AS number in the BGP AS-PATH attribute are replaced with the local AS number before sending a route update to that peer. Happens before routing filters and prepending. - `cisco_vpls_nlri_len_fmt` (String) VPLS NLRI length format type. Used for compatibility with Cisco VPLS. - `cluster_id` (String, Deprecated) In case this instance is a route reflector: the cluster ID of the router reflector cluster to this instance belongs. This attribute helps to recognize routing updates that come from another route reflector in this cluster and avoid routing information looping. Note that normally there is only one route reflector in a cluster; in this case, 'cluster-id' does not need to be configured and BGP router ID is used instead. - `comment` (String) - `disabled` (Boolean) - `hold_time` (String) Specifies the BGP Hold Time value to use when negotiating with peers. According to the BGP specification, if the router does not receive successive KEEPALIVE and/or UPDATE and/or NOTIFICATION messages within the period specified in the Hold Time field of the OPEN message, then the BGP connection to the peer will be closed. The minimal hold-time value of both peers will be actually used (note that the special value 0 or 'infinity' is lower than any other value) infinity - never expire the connection and never send keepalive messages. - `input` (Block List, Max: 1) A group of parameters associated with BGP input. (see [below for nested schema](#nestedblock--input)) - `keepalive_time` (String) How long to keep the BGP session open after the last received 'keepalive' message. - `multihop` (Boolean) Specifies whether the remote peer is more than one hop away. This option affects outgoing next-hop selection as described in RFC 4271 (for EBGP only, excluding EBGP peers local to the confederation). It also affects: whether to accept connections from peers that are not in the same network (the remote address of the connection is used for this check); whether to accept incoming routes with NEXT_HOP attribute that is not in the same network as the address used to establish the connection; the target-scope of the routes installed from this peer; routes from multi-hop or IBGP peers resolve their next-hops through IGP routes by default. - `nexthop_choice` (String) Affects the outgoing NEXT_HOP attribute selection. Note that next-hops set in filters always take precedence. Also note that the next-hop is not changed on route reflection, except when it's set in the filter. default - select the next-hop as described in RFC 4271 force-self - always use a local address of the interface that is used to connect to the peer as the next-hop; propagate - try to propagate further the next-hop received; i.e. if the route has BGP NEXT_HOP attribute, then use it as the next-hop, otherwise, fall back to the default case. - `output` (Block List, Max: 1) A group of parameters associated with BGP output. (see [below for nested schema](#nestedblock--output)) - `remove_private_as` (Boolean) If set, then the BGP AS-PATH attribute is removed before sending out route updates if the attribute contains only private AS numbers. The removal process happens before routing filters are applied and before the local, AS number is prepended to the AS path. - `router_id` (String, Deprecated) BGP Router ID to be used. Use the ID from the /routing/router-id configuration by specifying the reference name, or set the ID directly by specifying IP. Equal router-ids are also used to group peers into one instance. - `routing_table` (String) Name of the routing table, to install routes in. - `save_to` (String) Filename to be used to save BGP protocol-specific packet content (Exported PDU) into pcap file. This method allows much simpler peer-specific packet capturing for debugging purposes. Pcap files in this format can also be loaded to create virtual BGP peers to recreate conditions that happened at the time when packet capture was running. - `templates` (Set of String) List of template names from which to inherit parameters. Useful feature, to easily configure groups with overlapping configuration options. - `use_bfd` (Boolean) Whether to use the BFD protocol for faster connection state detection. - `vrf` (String) The VRF table this resource operates on. ### Read-Only - `default` (Boolean) It's the default item. - `id` (String) The ID of this resource. ### Nested Schema for `input` Optional: - `accept_comunities` (String) A quick way to filter incoming updates with specific communities. It allows filtering incoming messages directly before they are even parsed and stored in memory, that way significantly reducing memory usage. Regular input filter chain can only reject prefixes which means that it will still eat memory and will be visible in /routing route table as 'not active, filtered'. Changes to be applied required session refresh. - `accept_ext_communities` (String) A quick way to filter incoming updates with specific extended communities. It allows filtering incoming messages directly before they are even parsed and stored in memory, that way significantly reducing memory usage. Regular input filter chain can only reject prefixes which means that it will still eat memory and will be visible in /routing route table as 'not active, filtered'. Changes to be applied required session refresh. - `accept_large_comunities` (String) A quick way to filter incoming updates with specific large communities. It allows filtering incoming messages directly before they are even parsed and stored in memory, that way significantly reducing memory usage. Regular input filter chain can only reject prefixes which means that it will still eat memory and will be visible in /routing route table as 'not active, filtered'. Changes to be applied required session refresh. - `accept_nlri` (String) Name of the ipv4/6 address-list. A quick way to filter incoming updates with specific NLRIs. It allows filtering incoming messages directly before they are even parsed and stored in memory, that way significantly reducing memory usage. Regular input filter chain can only reject prefixes which means that it will still eat memory and will be visible in /routing route table as 'not active, filtered'. Changes to be applied required session restart. - `accept_unknown` (String, Deprecated) A quick way to filter incoming updates with specific 'unknown' attributes. It allows filtering incoming messages directly before they are even parsed and stored in memory, that way significantly reducing memory usage. Regular input filter chain can only reject prefixes which means that it will still eat memory and will be visible in /routing route table as 'not active, filtered'. Changes to be applied required session refresh. - `affinity` (String) Configure input multi-core processing. Read more in Routing Protocol Multi-core Support article. alone - input and output of each session are processed in its own process, most likely the best option when there are a lot of cores and a lot of peers afi, instance, vrf, remote-as - try to run input/output of new session in process with similar parameters main - run input/output in the main process (could potentially increase performance on single-core even possibly on multi-core devices with a small amount of cores) input - run output in the same process as input (can be set only for output affinity) - `allow_as` (Number) Indicates how many times to allow your own AS number in AS-PATH, before discarding a prefix. - `filter` (String) Name of the routing filter chain to be used on input prefixes. This happens after NLRIs are processed. If the chain is not specified, then BGP by default accepts everything. - `filter_communities` (String) A quick way to filter incoming updates with specific communities. It allows filtering incoming messages directly before they are even parsed and stored in memory, that way significantly reducing memory usage. Regular input filter chain can only reject prefixes which means that it will still eat memory and will be visible in /routing route table as "not active, filtered". Changes to be applied required session refresh. - `filter_ext_communities` (String) A quick way to filter incoming updates with specific extended communities. It allows filtering incoming messages directly before they are even parsed and stored in memory, that way significantly reducing memory usage. Regular input filter chain can only reject prefixes which means that it will still eat memory and will be visible in /routing route table as "not active, filtered". Changes to be applied required session refresh. - `filter_large_communities` (String) A quick way to filter incoming updates with specific large communities. It allows filtering incoming messages directly before they are even parsed and stored in memory, that way significantly reducing memory usage. Regular input filter chain can only reject prefixes which means that it will still eat memory and will be visible in /routing route table as "not active, filtered". Changes to be applied required session refresh. - `filter_nlri` (String) Name of the filter chain that will filter incoming IPv4/IPv6 NLRIs directly before they are stored in memory, that way significantly reducing memory usage. Regular input filter chain can only reject prefixes which means that it will still eat memory and will be visible in `/routing route` table as `not active, filtered`. Changes to be applied required session restart. - `filter_unknown` (String) A quick way to filter incoming updates with specific "unknown" attributes. It allows filtering incoming messages directly before they are even parsed and stored in memory, that way significantly reducing memory usage. Regular input filter chain can only reject prefixes which means that it will still eat memory and will be visible in /routing route table as "not active, filtered". Changes to be applied required session refresh. - `ignore_as_path_len` (Boolean, Deprecated) Whether to ignore the AS_PATH attribute in the BGP route selection algorithm - `limit_process_routes_ipv4` (Number) Try to limit the amount of received IPv4 routes to the specified number. This number does not represent the exact number of routes going to be installed in the routing table by the peer. BGP session 'clear' command must be used to reset the flag if the limit is reached. - `limit_process_routes_ipv6` (Number) Try to limit the amount of received IPv6 routes to the specified number. This number does not represent the exact number of routes going to be installed in the routing table by the peer. BGP session 'clear' command must be used to reset the flag if the limit is reached. ### Nested Schema for `output` Optional: - `affinity` (String) Configure output multicore processing. Read more in Routing Protocol Multi-core Support article. alone - input and output of each session is processed in its own process, the most likely best option when there are a lot of cores and a lot of peers afi, instance, vrf, remote-as - try to run input/output of new session in process with similar parameters main - run input/output in the main process (could potentially increase performance on single-core even possibly on multicore devices with small amount of cores) input - run output in the same process as input (can be set only for output affinity). - `default_originate` (String) Specifies default route (0.0.0.0/0) distribution method. - `default_prepend` (Number) The count of AS prepended to the AS path. - `filter_chain` (String) Name of the routing filter chain to be used on the output prefixes. If the chain is not specified, then BGP by default accepts everything. - `filter_select` (String) Name of the routing select chain to be used for prefix selection. If not specified, then default selection is used. - `keep_sent_attributes` (Boolean) Store in memory sent prefix attributes, required for ' dump-saved-advertisements ' command to work. By default, sent-out prefixes are not stored to preserve the router's memory. An option should be enabled only for debugging purposes when necessary to see currently advertised prefixes. - `network` (String) Name of the address list used to send local networks. The network is sent only if a matching IGP route exists in the routing table. - `no_client_to_client_reflection` (Boolean) Disable client-to-client route reflection in Route Reflector setups. - `no_early_cut` (Boolean) The early cut is the mechanism, to guess (based on default RFC behavior) what would happen with the sent NPLRI when received by the remote peer. If the algorithm determines that the NLRI is going to be dropped, a peer will not even try to send it. However such behavior may not be desired in specific scenarios, then then this option should be used to disable the early cut feature. - `redistribute` (String) Enable redistribution of specified route types. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/routing/bgp/template get [print show-ids]] terraform import routeros_routing_bgp_template.test *3 ``` ================================================ FILE: docs/resources/routing_bgp_vpn.md ================================================ # routeros_routing_bgp_vpn (Resource) ## Example Usage ```terraform resource "routeros_routing_bgp_vpn" "test" { disabled = false export { redistribute = "connected" route_targets = ["1:1"] } import { route_targets = ["1:2"] } label_allocation_policy = "per-vrf" name = "bgp-mpls-vpn-test" route_distinguisher = "1.2.3.4:1" vrf = "vrfTest1" } ``` ## Schema ### Required - `name` (String) VPN instance name. - `route_distinguisher` (String) Helps to distinguish between overlapping routes from multiple VRFs. Should be unique per VRF. Accepts 3 types of formats. ### Optional - `disabled` (Boolean) - `export` (Block List, Max: 1) A group of parameters associated with the route export. (see [below for nested schema](#nestedblock--export)) - `import` (Block List, Max: 1) A group of parameters associated with the route import. (see [below for nested schema](#nestedblock--import)) - `instance` (String) Name of the instance this VPN is assigned to. - `label_allocation_policy` (String) Label allocationpolicy. - `vrf` (String) The VRF table this resource operates on. ### Read-Only - `id` (String) The ID of this resource. - `inactive` (Boolean) ### Nested Schema for `export` Optional: - `filter_chain` (String) The name of the routing filter chain that is used to filter prefixes before exporting. - `filter_select` (String) The name of the select filter chain that is used to select prefixes to be exported exporting. - `redistribute` (String) Enable redistribution of specified route types from VRF to VPNv4. - `route_targets` (Set of String) List of route targets added when exporting VPNv4 routes. The accepted RT format is similar to the one for Route Distinguishers. ### Nested Schema for `import` Optional: - `filter_chain` (String) The name of the routing filter chain that is used to filter prefixes during import. - `route_targets` (Set of String) List of route targets that will be used to import VPNv4 routes. The accepted RT format is similar to the one for Route Distinguishers. - `router_id` (String) The router ID of the BGP instance that will be used for the BGP best path selection algorithm. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/routing/bgp/vpn get [print show-ids]] terraform import routeros_routing_bgp_vpn.test *3 #Or you can import a resource using one of its attributes terraform import routeros_routing_bgp_vpn.test "name=xxx" ``` ================================================ FILE: docs/resources/routing_filter_rule.md ================================================ # routeros_routing_filter_rule (Resource) ## Example Usage ```terraform resource "routeros_routing_filter_rule" "test" { chain = "testChain" rule = "if (dst in 192.168.1.0/24 && dst-len>24) {set distance +1; accept} else {set distance -1; accept}" comment = "comment" disabled = true } ``` ## Schema ### Required - `chain` (String) Chain name. - `rule` (String) Filter rule. ### Optional - `comment` (String) - `disabled` (Boolean) ### Read-Only - `id` (String) The ID of this resource. - `inactive` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> /routing/filter/rule/print show-ids terraform import routeros_routing_filter_rule.test "*0" #Or you can import a resource using one of its attributes terraform import routeros_routing_filter_rule.test "name=xxx" ``` ================================================ FILE: docs/resources/routing_id.md ================================================ # routeros_routing_id (Resource) ## Example Usage ```terraform resource "routeros_routing_id" "test" { name = "router-id-test" router_id = "10.10.10.10" select_dynamic_id = "any" } ``` ## Schema ### Required - `name` (String) Reference name. ### Optional - `comment` (String) - `disabled` (Boolean) ID reference is not used. - `router_id` (String) Parameter to explicitly set the Router ID. If not specified, it can be elected from one of the configured IP addresses on the router. - `select_dynamic_id` (String) States what IP addresses to use for ID election. - `select_from_vrf` (String) VRF from which to select IP addresses for the ID election. ### Read-Only - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `dynamic_id` (String) Currently selected ID. - `id` (String) The ID of this resource. - `inactive` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/routing/id get [print show-ids]] terraform import routeros_routing_id.test "*1" #Or you can import a resource using one of its attributes terraform import routeros_routing_id.test "name=router-id-test" ``` ================================================ FILE: docs/resources/routing_igmp_proxy_interface.md ================================================ # routeros_routing_igmp_proxy_interface (Resource) ## Example Usage ```terraform resource "routeros_routing_igmp_proxy_interface" "test" { alternative_subnets = ["0.0.0.1/32", "0.0.0.2/32"] disabled = true interface = "lo" threshold = 5 } ``` ## Schema ### Required - `interface` (String) Name of the interface. ### Optional - `alternative_subnets` (Set of String) By default, only packets from directly attached subnets are accepted. This parameter can be used to specify a list of alternative valid packet source subnets, both for data or IGMP packets. Has an effect only on the upstream interface. Should be used when the source of multicast data often is in a different IP network. - `comment` (String) - `disabled` (Boolean) - `threshold` (Number) Minimal TTL. Packets received with a lower TTL value are ignored. - `upstream` (Boolean) The interface is called `upstream` if it's in the direction of the root of the multicast tree. An IGMP forwarding router must have exactly one upstream interface configured. The upstream interface is used to send out IGMP membership requests. ### Read-Only - `id` (String) The ID of this resource. - `inactive` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/routing/igmp/proxy/interface get [print show-ids]] terraform import routeros_routing_igmp_proxy_interface.test *3 #Or you can import a resource using one of its attributes terraform import routeros_routing_igmp_proxy_interface.test "interface=xxx" ``` ================================================ FILE: docs/resources/routing_ospf_area.md ================================================ # routeros_routing_ospf_area (Resource) ## Example Usage ```terraform resource "routeros_routing_ospf_instance" "test_routing_ospf_instance" { name = "test_routing_ospf_instance" } resource "routeros_routing_ospf_area" "test_routing_ospf_area" { name = "test_routing_ospf_area" instance = routeros_routing_ospf_instance.test_routing_ospf_instance.name } ``` ## Schema ### Required - `instance` (String) Name of the OSPF instance this area belongs to. - `name` (String) Changing the name of this resource will force it to be recreated. > The links of other configuration properties to this resource may be lost! > Changing the name of the resource outside of a Terraform will result in a loss of control integrity for that resource! ### Optional - `area_id` (String) OSPF area identifier. - `comment` (String) - `default_cost` (Number) Default cost of injected LSAs into the area. - `disabled` (Boolean) - `no_summaries` (Boolean) If set then the area will not flood summary LSAs in the stub area. The correct value of this attribute may not be displayed in Winbox. Please check the parameters in the console! - `nssa_translate` (String) The parameter indicates which ABR will be used as a translator from type7 to type5 LSA. - `type` (String) The area type. ### Read-Only - `id` (String) The ID of this resource. - `inactive` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> /routing/ospf/area/print show-ids terraform import routeros_routing_ospf_area.test_routing_ospf_area "*0" #Or you can import a resource using one of its attributes terraform import routeros_routing_ospf_area.test_routing_ospf_area "name=xxx" ``` ================================================ FILE: docs/resources/routing_ospf_area_range.md ================================================ # routeros_routing_ospf_area_range (Resource) ## Example Usage ```terraform resource "routeros_routing_ospf_area_range" "test" { area = routeros_routing_ospf_area.ospf-area-1.name advertise = true prefix = "::/64" disabled = true } ``` ## Schema ### Required - `area` (String) The OSPF area associated with this range. ### Optional - `advertise` (Boolean) Whether to create a summary LSA and advertise it to the adjacent areas. - `comment` (String) - `cost` (Number) The cost of the summary LSA this range will createdefault - use the largest cost of all routes used (i.e. routes that fall within this range). - `disabled` (Boolean) - `prefix` (String) The network prefix of this range. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/routing/ospf/area/range get [print show-ids]] terraform import routeros_routing_ospf_area_range.test *3 #Or you can import a resource using one of its attributes terraform import routeros_routing_ospf_area_range.test "comment=xxx" ``` ================================================ FILE: docs/resources/routing_ospf_instance.md ================================================ # routeros_routing_ospf_instance (Resource) ## Example Usage ```terraform resource "routeros_routing_ospf_instance" "test_routing_ospf_instance" { name = "test_routing_ospf_instance" } ``` ## Schema ### Required - `name` (String) Changing the name of this resource will force it to be recreated. > The links of other configuration properties to this resource may be lost! > Changing the name of the resource outside of a Terraform will result in a loss of control integrity for that resource! ### Optional - `comment` (String) - `disabled` (Boolean) - `domain_id` (String) MPLS-related parameter. - `domain_tag` (Number) if set, then used in route redistribution (as route-tag in all external LSAs generated by this router), and in route calculation (all external LSAs having this route tag are ignored). Needed for interoperability with older Cisco systems. By default not set. - `in_filter_chain` (String) name of the routing filter chain used for incoming prefixes - `mpls_te_address` (String) the area used for MPLS traffic engineering. - `mpls_te_area` (String) the area used for MPLS traffic engineering. - `originate_default` (String) Specifies default route (0.0.0.0/0) distribution method. - `out_filter_chain` (String) name of the routing filter chain used for outgoing prefixes filtering. - `out_filter_select` (String) name of the routing filter select chain, used for output selection. - `redistribute` (Set of String) Enable redistribution of specific route types. - `router_id` (String) OSPF Router ID. Can be set explicitly as an IP address, or as the name of the router-id instance. - `routing_table` (String) Name of the routing table in use. - `version` (Number) OSPF version this instance will be running (v2 for IPv4, v3 for IPv6). - `vrf` (String) The VRF table this resource operates on. ### Read-Only - `id` (String) The ID of this resource. - `inactive` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> /routing/ospf/instance/print show-ids terraform import routeros_routing_ospf_instance.test_routing_ospf_instance "*0" #Or you can import a resource using one of its attributes terraform import routeros_routing_ospf_instance.test_routing_ospf_instance "name=xxx" ``` ================================================ FILE: docs/resources/routing_ospf_interface_template.md ================================================ # routeros_routing_ospf_interface_template (Resource) ## Example Usage ```terraform resource "routeros_routing_ospf_instance" "test_routing_ospf_instance" { name = "test_routing_ospf_instance" } resource "routeros_routing_ospf_area" "test_routing_ospf_area" { name = "test_routing_ospf_area" instance = routeros_routing_ospf_instance.test_routing_ospf_instance.name } resource "routeros_routing_ospf_interface_template" "test_routing_ospf_interface_template" { area = routeros_routing_ospf_area.test_routing_ospf_area.name } ``` ## Schema ### Required - `area` (String) The OSPF area to which the matching interface will be associated. ### Optional - `auth` (String) Specifies authentication method for OSPF protocol messages. - `auth_id` (Number) The key id is used to calculate message digest (used when MD5 or SHA authentication is enabled). - `auth_key` (String, Sensitive) The authentication key to be used, should match on all the neighbors of the network segment (available since RouterOS 7.x). - `authentication_key` (String, Sensitive) The authentication key to be used, should match on all the neighbors of the network segment (for versions before RouterOS 7.x). - `comment` (String) - `cost` (Number) Interface cost expressed as link state metric. - `dead_interval` (String) Specifies the interval after which a neighbor is declared dead. - `disabled` (Boolean) - `hello_interval` (String) The interval between HELLO packets that the router sends out this interface. - `instance_id` (Number) Interface cost expressed as link state metric. - `interfaces` (Set of String) Interfaces to match. - `networks` (Set of String) The network prefixes associated with the area. - `passive` (Boolean) If enabled, then do not send or receive OSPF traffic on the matching interfaces. The correct value of this attribute may not be displayed in Winbox. Please check the parameters in the console! - `prefix_list` (String) Name of the address list containing networks that should be advertised to the v3 interface. - `priority` (Number) Router's priority. Used to determine the designated router in a broadcast network. - `retransmit_interval` (String) Time interval the lost link state advertisement will be resent. - `transmit_delay` (String) Link-state transmit delay is the estimated time it takes to transmit a link-state update packet on the interface. - `type` (String) The OSPF network type on this interface. - `use_bfd` (Boolean) Whether to use the BFD protocol for faster connection state detection. - `vlink_neighbor_id` (String) Specifies the router-id of the neighbor which should be connected over the virtual link. - `vlink_transit_area` (String) A non-backbone area the two routers have in common over which the virtual link will be established. ### Read-Only - `id` (String) The ID of this resource. - `inactive` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> /routing/ospf/interface-template/print show-ids terraform import routeros_routing_ospf_interface_template.test_routing_ospf_interface_template "*0" #Or you can import a resource using one of its attributes terraform import routeros_routing_ospf_interface_template.test_routing_ospf_interface_template "name=xxx" ``` ================================================ FILE: docs/resources/routing_rule.md ================================================ # routeros_routing_rule (Resource) ## Example Usage ```terraform resource "routeros_routing_rule" "test" { dst_address = "192.168.1.0/24" action = "lookup-only-in-table" interface = "ether1" } ``` ## Schema ### Optional - `action` (String) An action to take on the matching packet: * drop - silently drop the packet. * lookup - perform a lookup in routing tables. * lookup-only-in-table - perform lookup only in the specified routing table (see table parameter). * unreachable - generate ICMP unreachable message and send it back to the source. - `comment` (String) - `disabled` (Boolean) - `dst_address` (String) The destination address of the packet to match. - `interface` (String) Incoming interface to match. - `min_prefix` (Number) Equivalent to Linux IP rule `suppress_prefixlength`. For example to suppress the default route in the routing decision set the value to 0. - `routing_mark` (String) Match specific routing mark. - `src_address` (String) The source address of the packet to match. - `table` (String) Name of the routing table to use for lookup. ### Read-Only - `id` (String) The ID of this resource. - `inactive` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/routing/rule get [print show-ids]] terraform import routeros_routing_rule.test *3 #Or you can import a resource using one of its attributes terraform import routeros_routing_rule.test "name=xxx" ``` ================================================ FILE: docs/resources/routing_table.md ================================================ # routeros_routing_table (Resource) ## Example Usage ```terraform resource "routeros_routing_table" "test_table" { name = "to_ISP1" fib = false } ``` ## Schema ### Optional - `comment` (String) - `disabled` (Boolean) - `fib` (Boolean) fib parameter should be specified if the routing table is intended to push routes to the FIB. - `name` (String) Routing table name. ### Read-Only - `dynamic` (Boolean) Configuration item created by software, not by management interface. It is not exported, and cannot be directly modified. - `id` (String) The ID of this resource. - `invalid` (Boolean) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/routing/table get [print show-ids]] terraform import routeros_routing_table.test_table "*0" #Or you can import a resource using one of its attributes terraform import routeros_routing_table.test_table "name=xxx" ``` ================================================ FILE: docs/resources/scheduler.md ================================================ # routeros_scheduler (Resource) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_system_scheduler](system_scheduler.md) ================================================ FILE: docs/resources/snmp.md ================================================ # routeros_snmp (Resource) ## Example Usage ```terraform resource "routeros_snmp" "test" { contact = "John D." enabled = true engine_id_suffix = "8a3c" location = "Backyard" trap_community = "private" trap_generators = "start-trap" trap_version = 3 } ``` ## Schema ### Optional - `contact` (String) Contact information. - `enabled` (Boolean) Used to disable/enable SNMP service - `engine_id_suffix` (String) Unique identifier for an SNMPv3 engine by configuring the suffix of the engine ID. - `location` (String) Location information. - `src_address` (String) Force the router to always use the same IP source address for all of the SNMP messages. - `trap_community` (String, Sensitive) Which communities configured in community menu to use when sending out the trap. This name must be present in the community list. - `trap_generators` (String) What action will generate traps: * interfaces - interface changes; * start-trap - snmp server starting on the router. - `trap_interfaces` (String) List of interfaces that traps are going to be sent out. - `trap_target` (Set of String) IP (IPv4 or IPv6) addresses of SNMP data collectors that have to receive the trap. - `trap_version` (Number) Version of SNMP protocol to use for trap. - `vrf` (String) The VRF table this resource operates on. ### Read-Only - `engine_id` (String) For SNMP v3, used as part of identifier. You can configure suffix part of engine id using this argument. If SNMP client is not capable to detect set engine-id value then this prefix hex have to be used 0x80003a8c04 - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_snmp.test . ``` ================================================ FILE: docs/resources/snmp_community.md ================================================ # routeros_snmp_community (Resource) ## Example Usage ```terraform resource "routeros_snmp_community" "test" { authentication_password = "authpasswd" authentication_protocol = "MD5" comment = "Comment" disabled = true encryption_password = "encpassword" encryption_protocol = "DES" name = "private" read_access = true security = "private" write_access = true } resource "routeros_snmp_community" "mything" { addresses = ["10.0.1.12", "10.10.0.129"] name = "mything" } ``` ## Schema ### Optional - `addresses` (Set of String) Set of IP (v4 or v6) addresses or CIDR networks from which connections to SNMP server are allowed. - `authentication_password` (String, Sensitive) Password used to authenticate the connection to the server (SNMPv3). - `authentication_protocol` (String) The protocol used for authentication (SNMPv3). - `comment` (String) - `disabled` (Boolean) - `encryption_password` (String, Sensitive) The password used for encryption (SNMPv3). - `encryption_protocol` (String) encryption protocol to be used to encrypt the communication (SNMPv3). AES (see rfc3826) available since v6.16. - `name` (String) Community Name. - `read_access` (Boolean) Whether read access is enabled for this community. - `security` (String) Security features. - `write_access` (Boolean) Whether write access is enabled for this community. ### Read-Only - `default` (Boolean) It's the default item. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/snmp/community get [print show-ids]] terraform import routeros_snmp_community.test "*0" #Or you can import a resource using one of its attributes terraform import routeros_snmp_community.test "name=xxx" ``` ================================================ FILE: docs/resources/system_certificate.md ================================================ # routeros_system_certificate (Resource) Certificate resource management consists of two independent processes: * key creation and certificate signing request (`key` + `csr`) * certificate signing by the issuer (`crt`) For a complete certificate creation cycle, both of the above steps must be performed. In this case the `sign {}` block must be specified in the configuration. If you need to import the current state of the certificate resource, then do not specify the `sign{}` block. Importing an external certificate is also done without specifying the `sign{}` block, because the certificate should have already been signed by the issuer at this step. --- ## Example Usage ```terraform resource "routeros_system_certificate" "root_ca" { name = "Test-Root-CA" common_name = "RootCA" key_usage = ["key-cert-sign", "crl-sign"] trusted = true # Sign Root CA. sign { } } # digitalSignature: Used for entity and data origin authentication with integrity. # keyEncipherment: Used to encrypt symmetric key, which is then transferred to target. # keyAgreement: Enables use of key agreement to establish symmetric key with target. resource "routeros_system_certificate" "server_crt" { name = "Server-Certificate" common_name = "server.crt" # KUs: igitalSignature, keyEncipherment or keyAgreement key_usage = ["digital-signature", "key-encipherment", "tls-server"] sign { ca = routeros_system_certificate.root_ca.name } } resource "routeros_system_certificate" "client_crt" { name = "Client-Certificate" common_name = "client.crt" key_size = "prime256v1" # KUs: digitalSignature and/or keyAgreement key_usage = ["digital-signature", "key-agreement", "tls-client"] sign { ca = routeros_system_certificate.root_ca.name } } resource "routeros_system_certificate" "unsigned_crt" { name = "Unsigned-Certificate" common_name = "unsigned.crt" key_size = "1024" subject_alt_name = "DNS:router.lan,DNS:myrouter.lan,IP:192.168.88.1" } resource "routeros_system_certificate" "scep_client" { name = "SCEP-Client" common_name = "scep-client.crt" key_usage = ["digital-signature", "key-agreement", "tls-client"] sign_via_scep { scep_url = "http://scep.server/scep/test" } } # Import certificate data "routeros_x509" "cert" { data = < ## Schema ### Required - `common_name` (String) Common Name (e.g. server FQDN or YOUR name). - `name` (String) Name of the certificate. Name can be edited. ### Optional - `copy_from` (String) - `country` (String) Country Name (2 letter code). - `days_valid` (Number) Certificate lifetime. - `import` (Block Set, Max: 1) (see [below for nested schema](#nestedblock--import)) - `key_size` (String) - `key_usage` (Set of String) Detailed key usage descriptions can be found in RFC 5280. - `locality` (String) Locality Name (eg, city). - `organization` (String) Organizational Unit Name (eg, section) - `sign` (Block Set) (see [below for nested schema](#nestedblock--sign)) - `sign_via_scep` (Block Set) (see [below for nested schema](#nestedblock--sign_via_scep)) - `state` (String) State or Province Name (full name). - `subject_alt_name` (String) SANs (subject alternative names). - `trusted` (Boolean) If set to yes certificate is included 'in trusted certificate chain'. - `unit` (String) Organizational Unit Name (eg, section). ### Read-Only - `akid` (String) Authority Key Identifier. - `authority` (String) - `ca` (String) - `ca_crl_host` (String) - `ca_fingerprint` (String) - `challenge_password` (String, Sensitive) A challenge password for scep client. - `crl` (String) - `digest_algorithm` (Boolean) - `dsa` (Boolean) - `expired` (Boolean) Set to true if certificate is expired. - `expires_after` (String) - `fingerprint` (String) - `id` (String) The ID of this resource. - `invalid_after` (String) The date after which certificate wil be invalid. - `invalid_before` (String) The date before which certificate is invalid. - `issued` (String) - `issuer` (String) - `key_type` (String) - `private_key` (Boolean) - `req_fingerprint` (String) - `revoked` (String) - `scep_url` (String) - `serial_number` (String) - `skid` (String) Subject Key Identifier. - `smart_card_key` (String) - `status` (String) Shows current status of scep client. ### Nested Schema for `import` Optional: - `cert_file_content` (String) Certificate in PEM format. - `cert_file_name` (String) Certificate file name that will be imported. - `key_file_content` (String) Key in PEM format. - `key_file_name` (String) Key file name that will be imported. - `passphrase` (String, Sensitive) File passphrase if there is such. ### Nested Schema for `sign` Optional: - `ca` (String) Which CA to use if signing issued certificates. - `ca_crl_host` (String) CRL host if issuing CA certificate. ### Nested Schema for `sign_via_scep` Required: - `scep_url` (String) HTTP URL to the SCEP server. Optional: - `ca_identity` (String) SCEP CA identity. - `challenge_password` (String, Sensitive) A challenge password. - `on_smart_card` (Boolean) Whether to store a private key on smart card if hardware supports it. - `refresh` (Boolean) Check certificate expiration and refresh it if expired. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/certificate get [print show-ids]] #If you plan to manipulate the certificate requiring signing, you need to correctly fill in the sign{} section. #Changes in the sign{} section will not cause changes in the certificate. It's not a bug, it's a feature! terraform import routeros_system_certificate.client *9D #Or you can import a resource using one of its attributes terraform import routeros_system_certificate.client "name=xxx" ``` ================================================ FILE: docs/resources/system_certificate_scep_server.md ================================================ # routeros_system_certificate_scep_server (Resource) ## Example Usage ```terraform resource "routeros_system_certificate" "example_root_ca" { name = "example_root_ca" common_name = "Example Root CA" key_usage = ["key-cert-sign", "crl-sign"] trusted = true sign { } } # You can also use the alias "routeros_certificate_scep_server" resource "routeros_system_certificate_scep_server" "example_scep_server" { ca_cert = routeros_system_certificate.example_root_ca.name path = "/scep/example_scep_server" days_valid = 30 } ``` ## Schema ### Required - `ca_cert` (String) Name of the CA certificate to use. - `path` (String) HTTP path starting with `/scep/`. ### Optional - `days_valid` (Number) The number of days to sign certificates for. - `disabled` (Boolean) - `next_ca_cert` (String) Name of the next CA certificate or `none`. - `request_lifetime` (String) Request lifetime (5m minimum). ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell # The ID can be found via API or the terminal # The command for the terminal is -> /certificate/scep-server/print show-ids terraform import routeros_system_certificate_scep_server.example_scep_server "*1" #Or you can import a resource using one of its attributes terraform import routeros_system_certificate_scep_server.example_scep_server "name=xxx" ``` ================================================ FILE: docs/resources/system_clock.md ================================================ # routeros_system_clock (Resource) ## Example Usage ```terraform resource "routeros_system_clock" "set" { date = "2024-05-15" time = "17:58:11" time_zone_name = "EST" } ``` ## Schema ### Optional - `date` (String) Date. - `time` (String) Time. - `time_zone_autodetect` (Boolean) Feature available from v6.27. If enabled, the time zone will be set automatically. - `time_zone_name` (String) Name of the time zone. As most of the text values in RouterOS, this value is case sensitive. Special value manual applies [manually configured GMT offset](https://wiki.mikrotik.com/wiki/Manual:System/Time#Manual_time_zone_configuration), which by default is 00:00 with no daylight saving time. ### Read-Only - `dst_active` (Boolean) This property has the value yes while daylight saving time of the current time zone is active. - `gmt_offset` (String) This is the current value of GMT offset used by the system, after applying base time zone offset and active daylight saving time offset. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_system_clock.set . ``` ================================================ FILE: docs/resources/system_identity.md ================================================ # routeros_system_identity (Resource) ## Example Usage ```terraform resource "routeros_system_identity" "identity" { name = "My Router" } ``` ## Schema ### Required - `name` (String) Device name. ### Optional ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_system_identity.identity . ``` ================================================ FILE: docs/resources/system_led.md ================================================ # routeros_system_led (Resource) ## Example Usage ```terraform resource "routeros_system_led" "sfp1" { interface = "sfp1" leds = ["sfp-led"] type = "interface-activity" } ``` ## Schema ### Optional - `disabled` (Boolean) - `interface` (String) An option to set the interface to which the LED is connected. - `leds` (List of String) An option to set the LED name. - `modem_signal_treshold` (Number) An option to set the signal strength threshold for the modem LED. - `type` (String) An option to set the LED type. ### Read-Only - `default` (Boolean) It's the default item. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/system/leds get [print show-ids]] terraform import routeros_system_led.sfp1 '*1' #Or you can import a resource using one of its attributes terraform import routeros_system_led.sfp1 "name=xxx" ``` ================================================ FILE: docs/resources/system_led_settings.md ================================================ # routeros_system_led_settings (Resource) ## Example Usage ```terraform resource "routeros_system_led_settings" "settings" { all_leds_off = "immediate" } ``` ## Schema ### Optional - `all_leds_off` (String) An option to set when all LEDs should be turned off. Possible values: `after-1h`, `after-1min`, `immediate`, `never`. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_system_led_settings.settings . ``` ================================================ FILE: docs/resources/system_logging.md ================================================ # routeros_system_logging (Resource) ## Example Usage ```terraform resource "routeros_system_logging" "log_snmp_disk" { action = "disk" topics = ["snmp"] } ``` ## Schema ### Required - `action` (String) specifies one of the system default actions or user specified action listed in actions menu ### Optional - `disabled` (Boolean) - `prefix` (String) prefix added at the beginning of log messages - `regex` (String) Regex which will be used in order to match or not match message. If the regex is not matched, then even if topic is configured to be logged, but log message does not match regex, action will not be performed. - `topics` (Set of String) log all messages that falls into specified topic or list of topics. '!' character can be used before topic to exclude messages falling under this topic. For example, we want to log NTP debug info without too much details: /system logging add topics=ntp,debug,!packet ### Read-Only - `default` (Boolean) It's the default item. - `id` (String) The ID of this resource. - `invalid` (Boolean) ## Import Import is supported using the following syntax: ```shell # The ID can be found via API or the terminal # The command for the terminal is -> :put [/system/logging get [print show-ids]] terraform import routeros_system_logging.log_snmp_disk "*4" ``` ================================================ FILE: docs/resources/system_logging_action.md ================================================ # routeros_system_logging_action (Resource) ## Schema ### Required - `name` (String) Name of an action. - `target` (String) Storage facility or target of log messages. ### Optional - `bsd_syslog` (Boolean, Deprecated) Whether to use bsd-syslog as defined in RFC 3164. - `cef_event_delimiter` (String) Option helps remote syslog to distinguish between individual events within sent batch - `disk_file_count` (Number) Specifies number of files used to store log messages, applicable only if `action=disk`. - `disk_file_name` (String) Name of the file used to store log messages, applicable only if `action=disk`. - `disk_lines_per_file` (Number) Specifies maximum size of file in lines, applicable only if `action=disk`. - `disk_stop_on_full` (Boolean) Whether to stop to save log messages to disk after the specified disk-lines-per-file and disk-file-count number is reached, applicable only if `action=disk`. - `email_start_tls` (Boolean) Whether to use tls when sending email, applicable only if `action=email`. - `email_to` (String) Email address where logs are sent, applicable only if `action=email`. - `memory_lines` (Number) Number of records in local memory buffer, applicable only if `action=memory`. - `memory_stop_on_full` (Boolean) Whether to stop to save log messages in local buffer after the specified memory-lines number is reached. - `remember` (Boolean) Whether to keep log messages, which have not yet been displayed in console, applicable if `action=echo`. - `remote` (String) Remote logging server's IP/IPv6 address, applicable if `action=remote`. - `remote_log_format` (String) Format for logs to be sent to remote instance: - **cef** - logs are sent in CEF format; - **default** - logs are sent as it is; - **syslog** - logs are sent in BSD-syslog format. - `remote_port` (Number) Remote logging server's UDP port, applicable if `action=remote`. - `remote_protocol` (String) Protocol for remote logging messages. - `src_address` (String) Source address used when sending packets to remote server, applicable if `action=remote`. - `syslog_facility` (String) SYSLOG facility, applicable if `action=remote`. - `syslog_severity` (String) Severity level indicator defined in RFC 3164, applicable if `action=remote`. - `syslog_time_format` (String) SYSLOG time format (`bsd-syslog` or `iso8601`). - `vrf` (String) The VRF table this resource operates on. ### Read-Only - `default` (Boolean) It's the default item. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell # The ID can be found via API or the terminal # The command for the terminal is -> :put [/system/logging/action get [print show-ids]] terraform import routeros_system_logging_action.disk "*1" ``` ================================================ FILE: docs/resources/system_note.md ================================================ # routeros_system_note (Resource) ## Example Usage ```terraform resource "routeros_system_note" "test" { note = "For authorized use only." show_at_login = true show_at_cli_login = true } ``` ## Schema ### Required - `note` (String) Note that will be displayed. ### Optional - `show_at_cli_login` (Boolean) Whether to show system note before telnet login prompt. - `show_at_login` (Boolean) Whether to show system note on each login. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_system_note.test . ``` ================================================ FILE: docs/resources/system_ntp_client.md ================================================ # routeros_system_ntp_client (Resource) ## Example Usage ```terraform resource "routeros_system_ntp_client" "test" { enabled = true mode = "unicast" servers = ["146.59.35.38", "167.235.201.139"] } ``` ## Schema ### Optional - `enabled` (Boolean) Enable NTP client. - `mode` (String) Mode that the NTP client will operate in - `servers` (Set of String) The list of NTP servers. It is possible to add static entries.The following formats are accepted: * FQDN ("Resolved Address" will appear in the "Servers"- window in an appropriate column if the address is resolved) or IP address can be used. If DHCP-Client property `use-peer-ntp=yes` - the dynamic entries advertised by DHCP * ipv4 * ipv4@vrf * ipv6 * ipv6@vrf * ipv6-linklocal%interface - `vrf` (String) The VRF table this resource operates on. ### Read-Only - `freq_drift` (String) The fractional frequency drift per unit time. - `id` (String) The ID of this resource. - `status` (String) Current status of the NTP client. - `synced_server` (String) The IP address of the NTP Server. - `synced_stratum` (String) The accuracy of each server is defined by a number called the stratum, with the topmost level (primary servers) assigned as one and each level downwards (secondary servers) in the hierarchy assigned as one greater than the preceding level. - `system_offset` (String) This is a signed, fixed-point number indicating the offset of the NTP server's clock relative to the local clock, in seconds. ## Import Import is supported using the following syntax: ```shell terraform import routeros_system_ntp_client.test . ``` ================================================ FILE: docs/resources/system_ntp_server.md ================================================ # routeros_system_ntp_server (Resource) ## Example Usage ```terraform resource "routeros_system_ntp_server" "test" { enabled = true broadcast = true multicast = true manycast = true use_local_clock = true local_clock_stratum = 3 } ``` ## Schema ### Optional - `auth_key` (String) NTP symmetric key, used for authentication between the NTP client and server. Key Identifier (Key ID) - an integer identifying the cryptographic key used to generate the message-authentication code. - `broadcast` (Boolean) Enable certain NTP server mode, for this mode to work you have to set up broadcast-addresses field. - `broadcast_addresses` (String) Set broadcast address to use for NTP server broadcast mode. - `enabled` (Boolean) Enable NTP server. - `local_clock_stratum` (Number) Manually set stratum if ```use_local_clock = true```. - `manycast` (Boolean) Enable certain NTP server mode. - `multicast` (Boolean) Enable certain NTP server mode. - `use_local_clock` (Boolean) The server will supply its local system time as valid if others are not available. - `vrf` (String) The VRF table this resource operates on. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_system_ntp_server.test . ``` ================================================ FILE: docs/resources/system_routerboard_button_mode.md ================================================ # routeros_system_routerboard_button_mode (Resource) ## Example Usage ```terraform resource "routeros_system_script" "mode_button" { name = "mode-button" source = < ## Schema ### Optional - `enabled` (Boolean) An option to enable the operation of the button. - `hold_time` (String) An option to define the period within which the button should be pressed. - `on_event` (String) An option to set the script that will be run upon pressing the button. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_system_routerboard_button_mode.settings . ``` ================================================ FILE: docs/resources/system_routerboard_button_reset.md ================================================ # routeros_system_routerboard_button_reset (Resource) ## Example Usage ```terraform resource "routeros_system_script" "reset_button" { name = "reset-button" source = < ## Schema ### Optional - `enabled` (Boolean) An option to enable the operation of the button. - `hold_time` (String) An option to define the period within which the button should be pressed. - `on_event` (String) An option to set the script that will be run upon pressing the button. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_system_routerboard_button_reset.settings . ``` ================================================ FILE: docs/resources/system_routerboard_button_wps.md ================================================ # routeros_system_routerboard_button_wps (Resource) ## Example Usage ```terraform resource "routeros_system_script" "wps_button" { name = "wps-button" source = < ## Schema ### Optional - `enabled` (Boolean) An option to enable the operation of the button. - `hold_time` (String) An option to define the period within which the button should be pressed. - `on_event` (String) An option to set the script that will be run upon pressing the button. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_system_routerboard_button_wps.settings . ``` ================================================ FILE: docs/resources/system_routerboard_settings.md ================================================ # routeros_system_routerboard_settings (Resource) ## Example Usage ```terraform resource "routeros_system_routerboard_settings" "settings" { auto_upgrade = false silent_boot = true } ``` ## Schema ### Optional - `auto_upgrade` (Boolean) An option to enable firmware upgrade automatically after the RouterOS upgrade. - `baud_rate` (Number) An option to choose the onboard RS232 speed in bits per second. - `boot_delay` (String) A delay for a keystroke while booting. - `boot_device` (String) An option to choose the way RouterBOOT loads the operating system. Possible values: `ethernet`, `flash-boot`, `flash-boot-once-then-nand`, `nand-if-fail-then-ethernet`, `nand-only`, `try-ethernet-once-then-nand`. - `boot_os` (String) An option to choose the booting operating system for CRS3xx series switches. Possible values: `router-os`, `swos`. - `boot_protocol` (String) Boot protocol to use. Possible values: `bootp`, `dhcp`. - `cpu_frequency` (String) An option to change the CPU frequency of the device. - `cpu_mode` (String) An option whether to enter CPU suspend mode in HTL instruction. Possible values: `power-save`, `regular`. - `enable_jumper_reset` (Boolean) An option to enable reset via the onboard jumper. - `enter_setup_on` (String) An option to set which key will cause the BIOS to enter configuration mode during boot delay. Possible values: `any-key`, `delete-key`. - `force_backup_booter` (Boolean) An option to use the backup RouterBOOT. - `init_delay` (String) An option to set a delay before the USB port is initialized. Used for mPCIe modems with RB9xx series devices only. - `memory_data_rate` (String) An option to change the memory data rate of the device. Values depend on the model. - `memory_frequency` (String) An option to change the memory frequency of the device. Values depend on the model. - `preboot_etherboot` (String) An option to enable preboot `etherboot`, which runs before the regular boot device. - `preboot_etherboot_server` (String) An option to instruct `preboot-etherboot` to accept only from the specified Netinstall server. - `protected_routerboot` (String) An option to disable any access to the RouterBOOT configuration settings over a console cable and disables the operation of the reset button to change the boot mode (Netinstall will be disabled). Possible values: `disabled`, `enabled`. - `reformat_hold_button` (String) An option to enable resetting everything by pressing the button at power-on for longer than the specified time but less than `reformat_hold_button_max.` - `reformat_hold_button_max` (String) See `reformat_hold_button`. - `regulatory_domain_ce` (Boolean) An option to enable extra-low TX power for high antenna gain devices. - `silent_boot` (Boolean) An option to turn off output on the serial console and beeping sounds during booting. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_system_routerboard_settings.settings . ``` ================================================ FILE: docs/resources/system_routerboard_usb.md ================================================ # routeros_system_routerboard_usb (Resource) ## Example Usage ```terraform resource "routeros_system_routerboard_usb" "settings" { type = "auto" } ``` ## Schema ### Optional - `type` (String) An option to set the type of the USB port. Possible value: `auto`, `mini-PCIe`, `USB-type-A`. - `usb_mode` (String) An option to set the USB port mode. Possible values: `automatic`, `force-host`. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_system_routerboard_usb.settings . ``` ================================================ FILE: docs/resources/system_scheduler.md ================================================ # routeros_system_scheduler (Resource) ## Example Usage ```terraform resource "routeros_system_scheduler" "schedule1" { name = "schedule1" on_event = "script name" } ``` ## Schema ### Required - `name` (String) Changing the name of this resource will force it to be recreated. > The links of other configuration properties to this resource may be lost! > Changing the name of the resource outside of a Terraform will result in a loss of control integrity for that resource! - `on_event` (String) Name of the script to execute. It must be presented at /system script. ### Optional - `comment` (String) - `disabled` (Boolean) - `interval` (String) Interval between two script executions, if time interval is set to zero, the script is only executed at its start time, otherwise it is executed repeatedly at the time interval is specified. - `policy` (List of String) List of applicable policies: * dude - Policy that grants rights to log in to dude server. * ftp - Policy that grants full rights to log in remotely via FTP, to read/write/erase files and to transfer files from/to the router. Should be used together with read/write policies. * password - Policy that grants rights to change the password. * policy - Policy that grants user management rights. Should be used together with the write policy. Allows also to see global variables created by other users (requires also 'test' policy). * read - Policy that grants read access to the router's configuration. All console commands that do not alter router's configuration are allowed. Doesn't affect FTP. * reboot - Policy that allows rebooting the router. * romon - Policy that grants rights to connect to RoMon server. * sensitive - Policy that grants rights to change "hide sensitive" option, if this policy is disabled sensitive information is not displayed. * sniff - Policy that grants rights to use packet sniffer tool. * test - Policy that grants rights to run ping, traceroute, bandwidth-test, wireless scan, snooper, and other test commands. * write - Policy that grants write access to the router's configuration, except for user management. This policy does not allow to read the configuration, so make sure to enable read policy as well. policy = ["ftp", "read", "write"] - `start_date` (String) Date of the first script execution. - `start_time` (String) Time of the first script execution. If scheduler item has start-time set to startup, it behaves as if start-time and start-date were set to time 3 seconds after console starts up. It means that all scripts having start-time is startup and interval is 0 will be executed once each time router boots. If the interval is set to value other than 0 scheduler will not run at startup. ### Read-Only - `id` (String) The ID of this resource. - `next_run` (String) - `owner` (String) - `run_count` (String) This counter is incremented each time the script is executed. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/system/scheduler get [print show-ids]] terraform import routeros_system_scheduler.schedule1 "*0" #Or you can import a resource using one of its attributes terraform import routeros_system_scheduler.schedule1 "name=xxx" ``` ================================================ FILE: docs/resources/system_script.md ================================================ # routeros_system_script (Resource) ## Example Usage ```terraform resource "routeros_system_script" "script" { name = "my_script" source = < ## Schema ### Required - `name` (String) Name of the script. - `source` (String) Script source code. ### Optional - `comment` (String) - `dont_require_permissions` (Boolean) Bypass permissions check when the script is being executed, useful when scripts are being executed from services that have limited permissions, such as Netwatch. - `launch_trigger` (String) Changing the attribute value causes the script to run. - `policy` (Set of String) List of applicable policies: * ftp - Policy that grants full rights to log in remotely via FTP, to read/write/erase files and to transfer files from/to the router. Should be used together with read/write policies. * password - Policy that grants rights to change the password. * policy - Policy that grants user management rights. Should be used together with the write policy. Allows also to see global variables created by other users (requires also 'test' policy). * read - Policy that grants read access to the router's configuration. All console commands that do not alter router's configuration are allowed. Doesn't affect FTP. * reboot - Policy that allows rebooting the router. * sensitive - Policy that grants rights to change "hide sensitive" option, if this policy is disabled sensitive information is not displayed. * sniff - Policy that grants rights to use packet sniffer tool. * test - Policy that grants rights to run ping, traceroute, bandwidth-test, wireless scan, snooper, and other test commands. * write - Policy that grants write access to the router's configuration, except for user management. This policy does not allow to read the configuration, so make sure to enable read policy as well. policy = ["ftp", "read", "write"] ### Read-Only - `id` (String) The ID of this resource. - `invalid` (Boolean) - `last_started` (String) Date and time when the script was last invoked. - `owner` (String) - `run_count` (String) This counter is incremented each time the script is executed. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/system/script get [print show-ids]] terraform import routeros_system_script.script "*0" #Or you can import a resource using one of its attributes terraform import routeros_system_script.script "name=xxx" ``` ================================================ FILE: docs/resources/system_user.md ================================================ # routeros_system_user (Resource) ## Example Usage ```terraform resource "routeros_system_user" "test" { name = "test-user-1" address = "0.0.0.0/0" group = "read" password = "secret" comment = "Test User" } ``` ## Schema ### Required - `group` (String) Name of the group the user belongs to. - `name` (String) User name. Although it must start with an alphanumeric character, it may contain '*', '_', '.' and '@' symbols. ### Optional - `address` (String) Host or network address from which the user is allowed to log in. - `comment` (String) - `disabled` (Boolean) - `inactivity_policy` (String) Inactivity policy. - `inactivity_timeout` (String) Inactivity timeout for non-GUI sessions. - `password` (String, Sensitive) User password. If not specified, it is left blank (hit [Enter] when logging in). It conforms to standard Unix characteristics of passwords and may contain letters, digits, '*' and '_' symbols. ### Read-Only - `expired` (Boolean) Password expired. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/user get [print show-ids]] terraform import routeros_system_user.test *1 #Or you can import a resource using one of its attributes terraform import routeros_system_user.test "name=xxx" ``` ================================================ FILE: docs/resources/system_user_aaa.md ================================================ # routeros_system_user_aaa (Resource) ## Example Usage ```terraform resource "routeros_system_user_aaa" "settings" { use_radius = true } ``` ## Schema ### Optional - `accounting` (Boolean) An option that enables accounting for users. - `default_group` (String) The user group that is used by default for users authenticated via a RADIUS server. - `exclude_groups` (Set of String) A set of groups that are not allowed for users authenticated by RADIUS. - `interim_update` (String) Interval between scheduled RADIUS Interim-Update messages. - `use_radius` (Boolean) An option whether to use RADIUS server. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_system_user_aaa.settings . ``` ================================================ FILE: docs/resources/system_user_group.md ================================================ # routeros_system_user_group (Resource) ## Example Usage ```terraform resource "routeros_system_user_group" "terraform" { name = "terraform" policy = ["api", "!ftp", "!local", "password", "policy", "read", "!reboot", "!rest-api", "!romon", "sensitive", "!sniff", "!ssh", "!telnet", "!test", "!web", "!winbox", "write"] } ``` ## Schema ### Required - `name` (String) The name of the user group ### Optional - `comment` (String) - `policy` (Set of String) A set of allowed policies. - `skin` (String) The name of the skin that will be used for WebFig. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/user/group get [print show-ids]] terraform import routeros_system_user_group.terraform *1 #Or you can import a resource using one of its attributes terraform import routeros_system_user_group.terraform "name=xxx" ``` ================================================ FILE: docs/resources/system_user_settings.md ================================================ # routeros_system_user_settings (Resource) ## Example Usage ```terraform resource "routeros_system_user_settings" "settings" { minimum_categories = 2 minimum_password_length = 8 } ``` ## Schema ### Optional - `minimum_categories` (Number) An option specifies the complexity requirements of the password, with categories being uppercase, lowercase, digit, and symbol. - `minimum_password_length` (Number) An option specifies the minimum length of the password. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_system_user_settings.settings . ``` ================================================ FILE: docs/resources/system_user_sshkeys.md ================================================ # routeros_system_user_sshkeys (Resource) ## Example Usage ```terraform resource "routeros_system_user_sshkeys" "test" { user = "test-user-1" key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCyJ1EvW98veNVzR3VamNgmu0xOd/JK9YNvP/pa4WC5eT90UbX4TN7dKEK/x2FCwnnG9u0FQhzG2qa/Cg8meUvlfydn6uxc0/WCeXTKSu6sT63noPO6m4fHY7gu3Zt+fOc/WYGch9sBeWjZlCS1mA2lajkWhM3J8TFWCFm2Zk4/S3s5mt6VLbwpQnH2LhE41+azzDEVhcR6i3FfdgOF/J+j2fYYHJsBEKoQA5zUac2zWmz7X4Rv/g11ZBRqdMpHSD58o5F9lBb13antu5GcEs5RXpXp08OyXuRV9qhFpDBC8DOMALSOgT3vnu8uJLgo8QIulERofj/cRXbLCsmvMbpioBuGFXWx3ha4Ntd6z07kUh2KVbaIQLd/629UHNvgIhoBLlREJ8E5vllsX+jh8hRITHcCiEwXcDO+gG3hvJt0+jm8S8SObE/IHk8VuwWdhIsSku5vd+wVlxm8VeJzjc0cjdIiytvsq8VpLudKEUiqR0f2tHcoq8H+xcJv3Ycu1i8=" comment = "Test User" } ``` ## Schema ### Required - `key` (String, Sensitive) key - `user` (String) username to which the SSH key is assigned. ### Optional - `comment` (String) ### Read-Only - `bits` (Number) key length - `fingerprint` (String) SSH key fingerprint - `id` (String) The ID of this resource. - `key_owner` (String) SSH key owner - `key_type` (String) key type - `rsa` (Boolean) key type is rsa ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/user/ssh-keys get [print show-ids]] terraform import routeros_system_user_sshkeys.test *1 #Or you can import a resource using one of its attributes terraform import routeros_system_user_sshkeys.test "name=xxx" ``` ================================================ FILE: docs/resources/tool_bandwidth_server.md ================================================ # routeros_tool_bandwidth_server (Resource) ## Example Usage ```terraform resource "routeros_tool_bandwidth_server" "test" { enabled = true authenticate = false max_sessions = 100 allocate_udp_ports_from = 2000 } ``` ## Schema ### Optional - `allocate_udp_ports_from` (Number) Beginning of UDP port range. - `allowed_addresses4` (Set of String) IPv4 allowed networks. - `allowed_addresses6` (Set of String) Ipv6 allowed networks. - `authenticate` (Boolean) Communicate only with authenticated clients. - `enabled` (Boolean) Defines whether bandwidth server is enabled or not. - `max_sessions` (Number) Maximal simultaneous test count. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_tool_bandwidth_server.test . ``` ================================================ FILE: docs/resources/tool_email.md ================================================ # routeros_tool_email (Resource) ## Schema ### Optional - `from` (String) Name or email address that will be shown as a receiver. - `password` (String, Sensitive) Password used for authenticating to an SMTP server. - `port` (String) SMTP server's port. - `server` (String) SMTP server's IP address. - `tls` (String) Whether to use TLS encryption: * yes - sends STARTTLS and drops the session if TLS is not available on the server * no - do not send STARTTLS * starttls - sends STARTTLS and continue without TLS if a server responds that TLS is not available - `user` (String) The username used for authenticating to an SMTP server. - `vrf` (String) The VRF table this resource operates on. ### Read-Only - `id` (String) The ID of this resource. - `last_address` (String) - `last_status` (String) ================================================ FILE: docs/resources/tool_graphing_interface.md ================================================ # routeros_tool_graphing_interface (Resource) ## Example Usage ```terraform resource "routeros_tool_graphing_interface" "test" { interface = "all" } ``` ## Schema ### Required - `interface` (String) Name of the interface. ### Optional - `allow_address` (String) IP address range from which is allowed to access graphing information. - `disabled` (Boolean) - `store_on_disk` (Boolean) Defines whether to store collected information on system drive. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/tool/graphing/interface get [print show-ids]] terraform import routeros_tool_graphing_interface.test "*0" ``` ================================================ FILE: docs/resources/tool_graphing_queue.md ================================================ # routeros_tool_graphing_queue (Resource) ## Example Usage ```terraform resource "routeros_tool_graphing_queue" "test" { simple_queue = "all" } ``` ## Schema ### Required - `simple_queue` (String) Defines which queues will be monitored. all means that all queues on router will be monitored. ### Optional - `allow_address` (String) IP address range from which is allowed to access graphing information. - `allow_target` (Boolean) Whether to allow access to graphs from queue's target-address. - `disabled` (Boolean) - `store_on_disk` (Boolean) Defines whether to store collected information on system drive. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/tool/graphing/queue get [print show-ids]] terraform import routeros_tool_graphing_queue.test "*0" ``` ================================================ FILE: docs/resources/tool_graphing_resource.md ================================================ # routeros_tool_graphing_resource (Resource) ## Example Usage ```terraform resource "routeros_tool_graphing_resource" "test" {} ``` ## Schema ### Optional - `allow_address` (String) IP address range from which is allowed to access graphing information. - `disabled` (Boolean) - `store_on_disk` (Boolean) Defines whether to store collected information on system drive. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/tool/graphing/resource get [print show-ids]] terraform import routeros_tool_graphing_resource.test "*0" ``` ================================================ FILE: docs/resources/tool_mac_server.md ================================================ # routeros_tool_mac_server (Resource) ## Example Usage ```terraform resource "routeros_tool_mac_server" "test" { allowed_interface_list = "LAN" } ``` ## Schema ### Required - `allowed_interface_list` (String) List of interfaces for MAC Telnet access. ### Optional ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_tool_mac_server.test . ``` ================================================ FILE: docs/resources/tool_mac_server_ping.md ================================================ # routeros_tool_mac_server_ping (Resource) ## Example Usage ```terraform resource "routeros_tool_mac_server_ping" "test" { enabled = false } ``` ## Schema ### Optional - `enabled` (Boolean) Whether to enable the MAC Ping server. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_tool_mac_server_ping.test . ``` ================================================ FILE: docs/resources/tool_mac_server_winbox.md ================================================ # routeros_tool_mac_server_winbox (Resource) ## Example Usage ```terraform resource "routeros_tool_mac_server_winbox" "test" { allowed_interface_list = "LAN" } ``` ## Schema ### Required - `allowed_interface_list` (String) List of interfaces for MAC WinBox access. ### Optional ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_tool_mac_server_winbox.test . ``` ================================================ FILE: docs/resources/tool_netwatch.md ================================================ # routeros_tool_netwatch (Resource) ## Example Usage ```terraform resource "routeros_tool_netwatch" "test" { name = "watch-google-pdns" type = "icmp" host = "8.8.8.8" interval = "30s" up_script = ":log info \"Ping to 8.8.8.8 successful\"" thr_max = "400ms" } ``` ## Schema ### Required - `host` (String) The IP address of the server to be probed. Formats: * ipv4 * ipv4@vrf * ipv6 * ipv6@vrf * ipv6-linklocal%interface - `name` (String) Task name. ### Optional - `accept_icmp_time_exceeded` (Boolean) If the ICMP `time exceeded` message should be considered a valid response. - `comment` (String) - `disabled` (Boolean) - `dns_server` (String) The DNS server that the probe should send its requests to, if not specified it will use the value from `/ip dns`. - `down_script` (String) Script to execute on the event of probe state change `OK` --> `fail`. - `early_failure_detection` (Boolean) Netwatch will not wait to finish all the packets to be processed to change probe status if it is already known that host will be considered as `down`. - `early_success_detection` (Boolean) Netwatch will not wait to finish all the packets to be processed to change probe status if it is already known that host will be considered as `up`. - `http_code_max` (Number) Response in the range [http-code-min , http-code-max] is a probe pass/OK; outside - a probe fail. See [mozilla-http-status](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status) or [rfc7231](https://datatracker.ietf.org/doc/html/rfc7231#section-6). - `http_code_min` (Number) OK/fail criteria for HTTP response code. - `interval` (String) The time interval between probe tests. - `packet_count` (Number) Total count of ICMP packets to send out within a single test. - `packet_interval` (String) The time between ICMP-request packet send. - `packet_size` (Number) The total size of the IP ICMP packet. - `port` (Number) TCP port (for both tcp-conn and http-get probes) - `record_type` (String) Record type that will be used for DNS probe. - `src_address` (String) Source IP address which the Netwatch will try to use in order to reach the host. If address is not present, then the host will be considered as `down`. - `start_delay` (String) Time to wait before starting probe (on add, enable, or system start). - `startup_delay` (String) Time to wait until starting Netwatch probe after system startup. - `test_script` (String) Script to execute at the end of every probe test. - `thr_avg` (String) Fail threshold for rtt-avg. - `thr_http_time` (String) Fail threshold for http-resp-time. - `thr_jitter` (String) Fail threshold for rtt-jitter. - `thr_loss_count` (Number) Fail threshold for loss-count. - `thr_loss_percent` (Number) Fail threshold for loss-percent. - `thr_max` (String) Fail threshold for rtt-max (a value above thr-max is a probe fail). - `thr_stdev` (String) Fail threshold for rtt-stdev. - `thr_tcp_conn_time` (String) Fail threshold for tcp-connect-time, the configuration uses microseconds, if the time unit is not specified (s/m/h), log and status pages display the same value in milliseconds. - `timeout` (String) Max time limit to wait for a response. - `ttl` (Number) Manually set time to live value for ICMP packet. - `type` (String) Type of the probe: * icmp - (ping-style) series of ICMP request-response with statistics * tcp-conn - test TCP connection (3-way handshake) to a server specified by IP and port * http-get - do an HTTP Get request and test for a range of correct replies * simple - simplified ICMP probe, with fewer options than **ICMP** type, used for backward compatibility with the older Netwatch version - `up_script` (String) Script to execute on the event of probe state change `fail` --> `OK`. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/tool/netwatch get [print show-ids]] terraform import routeros_tool_netwatch.test *3 #Or you can import a resource using one of its attributes terraform import routeros_tool_netwatch.test "name=xxx" ``` ================================================ FILE: docs/resources/tool_sniffer.md ================================================ # routeros_tool_sniffer (Resource) ## Example Usage ```terraform resource "routeros_tool_sniffer" "test" { enabled = true streaming_enabled = true streaming_server = "192.168.88.5:37008" filter_stream = true filter_interface = ["ether2"] filter_direction = "rx" filter_operator_between_entries = "and" } ``` ## Schema ### Optional - `enabled` (Boolean) Start packet capture. - `file_limit` (Number) File size limit. Sniffer will stop when a limit is reached. - `file_name` (String) Name of the file where sniffed packets will be saved. - `filter_cpu` (String) CPU core used as a filter. - `filter_direction` (String) Specifies which direction filtering will be applied. - `filter_dst_ip_address` (Set of String) Up to 16 IP destination addresses used as a filter. - `filter_dst_ipv6_address` (Set of String) Up to 16 IPv6 destination addresses used as a filter. - `filter_dst_mac_address` (Set of String) Up to 16 MAC destination addresses and MAC address masks used as a filter. - `filter_dst_port` (Set of String) Up to 16 comma-separated destination ports used as a filter. A list of predefined port names is also available, like ssh and telnet. - `filter_interface` (Set of String) Interface name on which sniffer will be running. all indicates that the sniffer will sniff packets on all interfaces. - `filter_ip_address` (Set of String) Up to 16 IP addresses used as a filter. - `filter_ip_protocol` (Set of String) Up to 16 comma-separated IP/IPv6 protocols used as a filter. IP protocols (instead of protocol names, protocol numbers can be used): * ipsec-ah - IPsec AH protocol * ipsec-esp - IPsec ESP protocol * ddp - datagram delivery protocol * egp - exterior gateway protocol * ggp - gateway-gateway protocol * gre - general routing encapsulation * hmp - host monitoring protocol * idpr-cmtp - idpr control message transport * icmp - internet control message protocol * icmpv6 - internet control message protocol v6 * igmp - internet group management protocol * ipencap - ip encapsulated in ip * ipip - ip encapsulation * encap - ip encapsulation * iso-tp4 - iso transport protocol class 4 * ospf - open shortest path first * pup - parc universal packet protocol * pim - protocol independent multicast * rspf - radio shortest path first * rdp - reliable datagram protocol * st - st datagram mode * tcp - transmission control protocol * udp - user datagram protocol * vmtp versatile message transport * vrrp - virtual router redundancy protocol * xns-idp - xerox xns idp * xtp - xpress transfer protocol - `filter_ipv6_address` (Set of String) Up to 16 IPv6 addresses used as a filter. - `filter_mac_address` (Set of String) Up to 16 MAC addresses and MAC address masks used as a filter. - `filter_mac_protocol` (Set of String) Up to 16 comma separated entries used as a filter. Mac protocols (instead of protocol names, protocol number can be used): * 802.2 - 802.2 Frames (0x0004) * arp - Address Resolution Protocol (0x0806) * homeplug-av - HomePlug AV MME (0x88E1) * ip - Internet Protocol version 4 (0x0800) * ipv6 - Internet Protocol Version 6 (0x86DD) * ipx - Internetwork Packet Exchange (0x8137) * lldp - Link Layer Discovery Protocol (0x88CC) * loop-protect - Loop Protect Protocol (0x9003) * mpls-multicast - MPLS multicast (0x8848) * mpls-unicast - MPLS unicast (0x8847) * packing-compr - Encapsulated packets with compressed IP packing (0x9001) * packing-simple - Encapsulated packets with simple IP packing (0x9000) * pppoe - PPPoE Session Stage (0x8864) * pppoe-discovery - PPPoE Discovery Stage (0x8863) * rarp - Reverse Address Resolution Protocol (0x8035) * service-vlan - Provider Bridging (IEEE 802.1ad) & Shortest Path Bridging IEEE 802.1aq (0x88A8) * vlan - VLAN-tagged frame (IEEE 802.1Q) and Shortest Path Bridging IEEE 802.1aq with NNI compatibility (0x8100) - `filter_operator_between_entries` (String) Changes the logic for filters with multiple entries. - `filter_port` (Set of String) Up to 16 comma-separated ports used as a filter. A list of predefined port names is also available, like ssh and telnet. - `filter_size` (String) Filters packets of specified size or size range in bytes. - `filter_src_ip_address` (Set of String) Up to 16 IP source addresses used as a filter. - `filter_src_ipv6_address` (Set of String) Up to 16 IPv6 source addresses used as a filter. - `filter_src_mac_address` (Set of String) Up to 16 MAC source addresses and MAC address masks used as a filter. - `filter_src_port` (Set of String) Up to 16 comma-separated source ports used as a filter. A list of predefined port names is also available, like ssh and telnet. - `filter_stream` (Boolean) Sniffed packets that are devised for the sniffer server are ignored. - `filter_vlan` (Set of Number) Up to 16 VLAN IDs used as a filter. - `max_packet_size` (Number) - `memory_limit` (Number) Memory amount used to store sniffed data. - `memory_scroll` (Boolean) Whether to rewrite older sniffed data when the memory limit is reached. - `only_headers` (Boolean) Save in the memory only the packet's headers, not the whole packet. - `streaming_enabled` (Boolean) Defines whether to send sniffed packets to the streaming server. - `streaming_server` (String) Tazmen Sniffer Protocol (TZSP) stream receiver. ### Read-Only - `id` (String) The ID of this resource. - `running` (Boolean) ## Import Import is supported using the following syntax: ```shell terraform import routeros_tool_sniffer.test . ``` ================================================ FILE: docs/resources/user_manager_advanced.md ================================================ # routeros_user_manager_advanced (Resource) ## Example Usage ```terraform resource "routeros_user_manager_advanced" "settings" { web_private_password = "password" web_private_username = "admin" } ``` ## Schema ### Optional - `paypal_allow` (Boolean) An option whether to enable PayPal functionality for User Manager. - `paypal_currency` (String) The currency related to price setting in which users will be billed. - `paypal_password` (String) The password of the PayPal API account. - `paypal_signature` (String) The signature of the PayPal API account. - `paypal_use_sandbox` (Boolean) An option whether to use PayPal's sandbox environment for testing purposes. - `paypal_user` (String) The username of the PayPal API account. - `web_private_password` (String) The password for accessing `/um/PRIVATE/` section over HTTP. - `web_private_username` (String) The username for accessing `/um/PRIVATE/` section over HTTP. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_user_manager_advanced.settings . ``` ================================================ FILE: docs/resources/user_manager_attribute.md ================================================ # routeros_user_manager_attribute (Resource) ## Example Usage ```terraform resource "routeros_user_manager_attribute" "mikrotik_wireless_comment" { name = "Mikrotik-Wireless-Comment" packet_types = ["access-accept"] type_id = 21 value_type = "string" } ``` ## Schema ### Required - `name` (String) The attribute's name. - `type_id` (Number) Attribute identification number from the specific vendor's attribute database. ### Optional - `packet_types` (Set of String) A set of `access-accept` and `access-challenge`. - `value_type` (String) The attribute's value type. - `vendor_id` (String) IANA allocated a specific enterprise identification number. ### Read-Only - `default` (Boolean) It's the default item. - `default_name` (String) The attribute's default name. - `id` (String) The ID of this resource. - `standard_name` (String) ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/user-manager/attribute get [print show-ids]] terraform routeros_user_manager_attribute.mikrotik_wireless_comment '*1' #Or you can import a resource using one of its attributes terraform import routeros_user_manager_attribute.mikrotik_wireless_comment "name=xxx" ``` ================================================ FILE: docs/resources/user_manager_database.md ================================================ # routeros_user_manager_database (Resource) ## Example Usage ```terraform resource "routeros_user_manager_database" "settings" { db_path = "/flash/user-manager5" } ``` ## Schema ### Required - `db_path` (String) Path to the location where database files will be stored. ### Optional ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_user_manager_database.settings . ``` ================================================ FILE: docs/resources/user_manager_limitation.md ================================================ # routeros_user_manager_limitation (Resource) ## Example Usage ```terraform resource "routeros_user_manager_limitation" "test" { name = "test" download_limit = 1024 upload_limit = 1024 uptime_limit = "10d" } ``` ## Schema ### Required - `name` (String) Unique name of the limitation. ### Optional - `download_limit` (Number) The total amount of traffic a user can download in bytes. - `rate_limit_burst_rx` (Number) - `rate_limit_burst_threshold_rx` (Number) - `rate_limit_burst_threshold_tx` (Number) - `rate_limit_burst_time_rx` (String) - `rate_limit_burst_time_tx` (String) - `rate_limit_burst_tx` (Number) - `rate_limit_min_rx` (Number) - `rate_limit_min_tx` (Number) - `rate_limit_priority` (Number) - `rate_limit_rx` (Number) - `rate_limit_tx` (Number) - `reset_counters_interval` (String) The interval from `reset_counters_start_time` when all associated user statistics are cleared. - `reset_counters_start_time` (String) Static date and time value from which `reset_counters_interval` is calculated. - `transfer_limit` (Number) The total amount of aggregated (download+upload) traffic in bytes. - `upload_limit` (Number) The total amount of traffic a user can upload in bytes. - `uptime_limit` (String) The total amount of uptime a user can stay active. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/user-manager/limitation get [print show-ids]] terraform import routeros_user_manager_limitation.test '*1' #Or you can import a resource using one of its attributes terraform import routeros_user_manager_limitation.test "name=xxx" ``` ================================================ FILE: docs/resources/user_manager_profile.md ================================================ # routeros_user_manager_profile (Resource) ## Example Usage ```terraform resource "routeros_user_manager_profile" "test" { name = "test" name_for_users = "Test" price = 0.02 } ``` ## Schema ### Required - `name` (String) Unique name of the profile. ### Optional - `name_for_users` (String) The name that will be shown to users in the web interface. - `override_shared_users` (String) An option whether to allow multiple sessions with the same user name. - `price` (Number) The price of the profile. - `starts_when` (String) The time when the profile becomes active (`assigned` - immediately when the profile entry is created, `first-auth` - upon first authentication request). - `validity` (String) The total amount of time a user can use this profile. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/user-manager/profile get [print show-ids]] terraform import routeros_user_manager_profile.test '*1' #Or you can import a resource using one of its attributes terraform import routeros_user_manager_profile.test "name=xxx" ``` ================================================ FILE: docs/resources/user_manager_profile_limitation.md ================================================ # routeros_user_manager_profile_limitation (Resource) ## Example Usage ```terraform resource "routeros_user_manager_limitation" "test" { name = "test" download_limit = 1024 upload_limit = 1024 uptime_limit = "10d" } resource "routeros_user_manager_profile" "test" { name = "test" name_for_users = "Test" price = 0.02 } resource "routeros_user_manager_profile_limitation" "weekend_night" { limitation = routeros_user_manager_limitation.test.name profile = routeros_user_manager_profile.test.name from_time = "0s" till_time = "6h" weekdays = [ "sunday", "saturday", ] } ``` ## Schema ### Required - `limitation` (String) The limitation name. - `profile` (String) The profile name. ### Optional - `from_time` (String) Time of the day when the limitation should take place. - `till_time` (String) Time of the day when the limitation should end. - `weekdays` (Set of String) Days of the week when the limitation is active. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/user-manager/profile-limitation get [print show-ids]] terraform import routeros_user_manager_profile_limitation.weekend_night '*1' #Or you can import a resource using one of its attributes terraform import routeros_user_manager_profile_limitation.weekend_night "name=xxx" ``` ================================================ FILE: docs/resources/user_manager_router.md ================================================ # routeros_user_manager_router (Resource) ## Example Usage ```terraform resource "routeros_user_manager_router" "test" { address = "127.0.0.1" name = "test" shared_secret = "password" } ``` ## Schema ### Required - `address` (String) IP address of the RADIUS client. - `name` (String) Unique name of the RADIUS client. ### Optional - `coa_port` (Number) Port number of CoA (Change of Authorization) communication. - `disabled` (Boolean) - `shared_secret` (String, Sensitive) The shared secret to secure communication. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/user-manager/router get [print show-ids]] terraform import routeros_user_manager_router.test '*1' #Or you can import a resource using one of its attributes terraform import routeros_user_manager_router.test "name=xxx" ``` ================================================ FILE: docs/resources/user_manager_settings.md ================================================ # routeros_user_manager_settings (Resource) ## Example Usage ```terraform resource "routeros_user_manager_settings" "settings" { enabled = true } ``` ## Schema ### Optional - `accounting_port` (Number) Port to listen for RADIUS accounting requests. - `authentication_port` (Number) Port to listen for RADIUS authentication requests. - `certificate` (String) Certificate for use in EAP TLS-type authentication methods. - `enabled` (Boolean) An option whether the User Manager functionality is enabled. - `require_message_auth` (String) An option whether to require `Message-Authenticator` in received Access-Accept/Challenge/Reject messages. - `use_profiles` (Boolean) An option whether to use Profiles and Limitations. When set to `false`, only User configuration is required to run User Manager. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_user_manager_settings.settings . ``` ================================================ FILE: docs/resources/user_manager_user.md ================================================ # routeros_user_manager_user (Resource) ## Example Usage ```terraform resource "routeros_user_manager_attribute" "mikrotik_wireless_comment" { name = "Mikrotik-Wireless-Comment" type_id = 21 value_type = "string" } resource "routeros_user_manager_attribute" "mikrotik_wireless_vlanid" { name = "Mikrotik-Wireless-VLANID" type_id = 26 value_type = "uint32" } resource "routeros_user_manager_user_group" "test" { name = "test" } resource "routeros_user_manager_user" "test" { attributes = [ "${routeros_user_manager_attribute.mikrotik_wireless_comment.name}:Test Group", "${routeros_user_manager_attribute.mikrotik_wireless_vlanid.name}:100", ] group = routeros_user_manager_user_group.test.name name = "test" password = "password" } ``` ## Schema ### Required - `name` (String) Username for session authentication. ### Optional - `attributes` (List of String) A custom set of colon-separated attributes with their values will be added to `Access-Accept` messages for users in this group. - `caller_id` (String) Allow user's authentication with a specific Calling-Station-Id value. - `comment` (String) - `disabled` (Boolean) - `group` (String) Name of the group the user is associated with. - `otp_secret` (String) A token of a one-time code that will be attached to the password. - `password` (String) The password of the user for session authentication. - `shared_users` (Number) The total amount of sessions the user can simultaneously establish. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/user-manager/user get [print show-ids]] terraform import routeros_user_manager_user.test '*1' #Or you can import a resource using one of its attributes terraform import routeros_user_manager_user.test "name=xxx" ``` ================================================ FILE: docs/resources/user_manager_user_group.md ================================================ # routeros_user_manager_user_group (Resource) ## Example Usage ```terraform resource "routeros_user_manager_attribute" "mikrotik_wireless_comment" { name = "Mikrotik-Wireless-Comment" type_id = 21 value_type = "string" } resource "routeros_user_manager_attribute" "mikrotik_wireless_vlanid" { name = "Mikrotik-Wireless-VLANID" type_id = 26 value_type = "uint32" } resource "routeros_user_manager_user_group" "test" { name = "test" attributes = [ "${routeros_user_manager_attribute.mikrotik_wireless_comment.name}:Test Group", "${routeros_user_manager_attribute.mikrotik_wireless_vlanid.name}:100", ] inner_auths = [ "ttls-chap", "ttls-pap", ] outer_auths = [ "chap", "pap", ] } ``` ## Schema ### Required - `name` (String) Unique name of the group. ### Optional - `attributes` (List of String) A custom set of colon-separated attributes with their values will be added to `Access-Accept` messages for users in this group. - `inner_auths` (Set of String) A set of allowed authentication methods for tunneled authentication methods (`ttls-pap`, `ttls-chap`, `ttls-mschap1`, `ttls-mschap2`, `peap-mschap2`). - `outer_auths` (Set of String) A set of allowed authentication methods (`pap`, `chap`, `mschap1`, `mschap2`, `eap-tls`, `eap-ttls`, `eap-peap`, `eap-mschap2`). ### Read-Only - `default` (Boolean) It's the default item. - `default_name` (String) The default name of the group. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/user-manager/user/group get [print show-ids]] terraform import routeros_user_manager_user_group.test '*1' #Or you can import a resource using one of its attributes terraform import routeros_user_manager_user_group.test "name=xxx" ``` ================================================ FILE: docs/resources/user_manager_user_profile.md ================================================ # routeros_user_manager_user_profile (Resource) ## Example Usage ```terraform resource "routeros_user_manager_profile" "test" { name = "test" } resource "routeros_user_manager_user" "test" { name = "test" } resource "routeros_user_manager_user_profile" "test" { profile = routeros_user_manager_profile.test.name user = routeros_user_manager_user.test.name } ``` ## Schema ### Required - `profile` (String) Name of the profile to assign to the user. - `user` (String) Name of the user to use the specified profile. ### Optional ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/user-manager/user-profile get [print show-ids]] terraform import routeros_user_manager_user_profile.test '*1' #Or you can import a resource using one of its attributes terraform import routeros_user_manager_user_profile.test "name=xxx" ``` ================================================ FILE: docs/resources/vlan.md ================================================ # routeros_vlan (Resource) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_interface_vlan](interface_vlan.md) ================================================ FILE: docs/resources/vrrp.md ================================================ # routeros_vrrp (Resource) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_interface_vrrp](interface_vrrp.md) ================================================ FILE: docs/resources/wifi.md ================================================ # routeros_wifi (Resource) *This resource requires a minimum version of RouterOS 7.13.* ## Example Usage ```terraform # If you need to add a reference to an existing configuration, each inline section contains a `config` parameter # where you can specify the name of the actual resource. # configuration = { # config = routeros_wifi_configuration.my-config.name # } resource "routeros_wifi" "wifi1" { configuration = { manager = "capsman" } name = "wifi1" } ``` ## Schema ### Required - `name` (String) Name of the interface. ### Optional - `aaa` (Map of String) AAA inline settings. - `arp` (String) Address Resolution Protocol mode: * disabled - the interface will not use ARP * enabled - the interface will use ARP * local-proxy-arp - the router performs proxy ARP on the interface and sends replies to the same interface * proxy-arp - the router performs proxy ARP on the interface and sends replies to other interfaces * reply-only - the interface will only reply to requests originated from matching IP address/MAC address combinations which are entered as static entries in the ARP table. No dynamic entries will be automatically stored in the ARP table. Therefore for communications to be successful, a valid static entry must already exist. - `arp_timeout` (String) ARP timeout is time how long ARP record is kept in ARP table after no packets are received from IP. Value auto equals to the value of arp-timeout in IP/Settings, default is 30s. Can use postfix `ms`, `s`, `m`, `h`, `d` for milliseconds, seconds, minutes, hours or days. If no postfix is set then seconds (s) is used. - `channel` (Map of String) Channel inline settings. - `comment` (String) - `configuration` (Map of String) Configuration inline settings. - `datapath` (Map of String) Datapath inline settings. - `deprioritize_unii_3_4` (Boolean) Whether to assign lower priority to channels with a control frequency of 5720 or 5825-5885 MHz. These channels are unsupported by some client devices, making their automatic selection undesirable. Defaults to `yes` in ETSI regulatory domains, elsewhere to `no`. - `disable_running_check` (Boolean) An option to set the running property to true if it is not disabled. - `disabled` (Boolean) - `interworking` (Map of String) Interworking inline settings. - `l2mtu` (Number) Layer2 Maximum transmission unit. [See](https://wiki.mikrotik.com/wiki/Maximum_Transmission_Unit_on_RouterBoards). - `mac_address` (String) MAC address (BSSID) to use for the interface. - `master_interface` (String) The corresponding master interface of the virtual one. - `mtu` (Number) Layer3 maximum transmission unit - `security` (Map of String) Security inline settings. - `steering` (Map of String) Steering inline settings. ### Read-Only - `bound` (Boolean) A flag whether the interface is currently available for the WiFi manager. - `default_name` (String) The interface's default name. - `id` (String) The ID of this resource. - `inactive` (Boolean) A flag whether the interface is currently inactive. - `master` (Boolean) A flag whether the interface is not a virtual one. - `radio_mac` (String) The MAC address of the associated radio. - `running` (Boolean) A flag whether the interface has established a link to another device. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wifi get [print show-ids]] terraform import routeros_wifi.wifi1 '*1' ``` ================================================ FILE: docs/resources/wifi_aaa.md ================================================ # routeros_wifi_aaa (Resource) *This resource requires a minimum version of RouterOS 7.13.* ## Example Usage ```terraform resource "routeros_wifi_aaa" "aaa1" { called_format = "S" name = "aaa1" password_format = "" username_format = "AA:AA:AA:AA:AA:AA" } ``` ## Schema ### Required - `name` (String) Name of the AAA profile. ### Optional - `called_format` (String) Format of the `Called-Station-Id` RADIUS attribute. - `calling_format` (String) Format of the `Calling-Station-Id` RADIUS attribute. - `comment` (String) - `disabled` (Boolean) - `interim_update` (String) Interval at which to send interim updates about traffic accounting to the RADIUS server. - `mac_caching` (String) Time to cache RADIUS server replies when MAC address authentication is enabled. - `nas_identifier` (String) Value of the `NAS-Identifier` RADIUS attribute. - `password_format` (String) Format of the `User-Password` RADIUS attribute. - `username_format` (String) Format of the `User-Name` RADIUS attribute. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wifi/aaa get [print show-ids]] terraform import routeros_wifi_aaa.aaa1 '*1' #Or you can import a resource using one of its attributes terraform import routeros_wifi_aaa.aaa1 "name=xxx" ``` ================================================ FILE: docs/resources/wifi_access_list.md ================================================ # routeros_wifi_access_list (Resource) *This resource requires a minimum version of RouterOS 7.13.* ## Example Usage ```terraform resource "routeros_wifi_access_list" "radius" { action = "query-radius" comment = "RADIUS" radius_accounting = true } ``` ## Schema ### Optional - `action` (String) An action to take when a client matches. - `allow_signal_out_of_range` (String) An option that permits the client's signal to be out of the range always or for some time interval. - `client_isolation` (Boolean) An option that specifies whether to deny forwarding data between clients connected to the same interface. - `comment` (String) - `disabled` (Boolean) - `interface` (String) Interface name to compare with an interface to which the client actually connects to. - `mac_address` (String) MAC address of the client. - `mac_address_mask` (String) MAC address mask to apply when comparing clients' addresses. - `passphrase` (String) PSK passphrase for the client if some PSK authentication algorithm is used. - `place_before` (String) Before which position the rule will be inserted. > Please check the effect of this option, as it does not work as you think! > Best way to use in conjunction with a data source. See [example](../data-sources/ip_firewall.md#example-usage). - `radius_accounting` (Boolean) An option that specifies if RADIUS traffic accounting should be used in case of RADIUS authentication of the client. - `signal_range` (String) The range in which the client signal must fall. - `ssid_regexp` (String) The regular expression to compare the actual SSID the client connects to. - `time` (String) Time of the day and days of the week when the rule is applicable. - `vlan_id` (Number) VLAN ID to use for VLAN tagging or `none`. ### Read-Only - `id` (String) The ID of this resource. - `last_logged_in` (String) Last time this client logged in. - `last_logged_out` (String) Last time this client logged out. - `match_count` (Number) Number of times this entry was matched. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wifi/access-list get [print show-ids]] terraform import routeros_wifi_access_list.radius '*1' #Or you can import a resource using one of its attributes terraform import routeros_wifi_access_list.radius "name=xxx" ``` ================================================ FILE: docs/resources/wifi_cap.md ================================================ # routeros_wifi_cap (Resource) *This resource requires a minimum version of RouterOS 7.13.* ## Example Usage ```terraform resource "routeros_wifi_cap" "settings" { enabled = true discovery_interfaces = ["bridge1"] } ``` ## Schema ### Optional - `caps_man_addresses` (List of String) List of Manager IP addresses that CAP will attempt to contact during discovery. - `caps_man_certificate_common_names` (List of String) List of manager certificate common names that CAP will connect to. - `caps_man_names` (List of String) An ordered list of CAPs Manager names that the CAP will connect to. - `certificate` (String) Certificate to use for authentication. - `discovery_interfaces` (Set of String) List of interfaces over which CAP should attempt to discover CAPs Manager. - `enabled` (Boolean) Disable or enable the CAP functionality. - `lock_to_caps_man` (Boolean) Lock CAP to the first CAPsMAN it connects to. - `slaves_datapath` (String) Name of the bridge interface the CAP will be added to. - `slaves_static` (Boolean) An option that creates static virtual interfaces. ### Read-Only - `current_caps_man_address` (String) Currently used CAPsMAN address. - `current_caps_man_identity` (String) Currently used CAPsMAN identity. - `id` (String) The ID of this resource. - `locked_caps_man_common_name` (String) Common name of the CAPsMAN that the CAP is locked to. - `requested_certificate` (String) Requested certificate. ## Import Import is supported using the following syntax: ```shell terraform import routeros_wifi_cap.settings . ``` ================================================ FILE: docs/resources/wifi_capsman.md ================================================ # routeros_wifi_capsman (Resource) *This resource requires a minimum version of RouterOS 7.13.* ## Example Usage ```terraform resource "routeros_wifi_capsman" "settings" { enabled = true interfaces = ["bridge1"] upgrade_policy = "suggest-same-version" } ``` ## Schema ### Optional - `ca_certificate` (String) Device CA certificate. - `certificate` (String) Device certificate. - `enabled` (Boolean) Disable or enable CAPsMAN functionality. - `interfaces` (List of String) List of interfaces on which CAPsMAN will listen for CAP connections. - `package_path` (String) Folder location for the RouterOS packages. For example, use '/upgrade' to specify the upgrade folder from the files section. If empty string is set, CAPsMAN can use built-in RouterOS packages, note that in this case only CAPs with the same architecture as CAPsMAN will be upgraded. - `require_peer_certificate` (Boolean) Require all connecting CAPs to have a valid certificate. - `upgrade_policy` (String) Upgrade policy options. ### Read-Only - `generated_ca_certificate` (String) Generated CA certificate. - `generated_certificate` (String) Generated CAPsMAN certificate. - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell terraform import routeros_wifi_capsman.settings . ``` ================================================ FILE: docs/resources/wifi_channel.md ================================================ # routeros_wifi_channel (Resource) *This resource requires a minimum version of RouterOS 7.13.* ## Example Usage ```terraform resource "routeros_wifi_channel" "channel1" { name = "1" band = "2ghz-n" frequency = [2412] secondary_frequency = ["disabled"] skip_dfs_channels = "disabled" width = "20mhz" } ``` ## Schema ### Required - `name` (String) Name of the channel. ### Optional - `band` (String) Frequency band and wireless standard that will be used by the AP. - `comment` (String) - `deprioritize_unii_3_4` (Boolean) Whether to assign lower priority to channels with a control frequency of 5720 or 5825-5885 MHz. These channels are unsupported by some client devices, making their automatic selection undesirable. Defaults to `yes` in ETSI regulatory domains, elsewhere to `no`. - `disabled` (Boolean) - `frequency` (List of String) Channel frequency value or range in MHz on which AP or station will operate. - `reselect_interval` (String) An option that specifies when the interface should rescan channel availability and select the most appropriate one to use. - `reselect_time` (String) Specifies the clock time when the interface should run "rescan channel availability" and select the most appropriate one to use. Specifying the clock time will allow the system to select this time dynamically and randomly. This helps to avoid a situation when many APs at the same time scan the network, select the same channel, and prefer to use it at the same time. reselect-time uses a background scan. The reselect process will choose the most suitable channel considering the number of networks in the channel, channel usage, and overlap with networks in adjacent channels. It can be used with a list of frequencies defined, or with frequency not set - using all supported frequencies. Example: - 01:00..01:30 → Would set the rescan of channels to run every night, once, randomly, between 01:00 AM to 01:30 AM, system clock time. - 14:00..14:30 → Would set the rescan of channels to run every day (after midday), once, randomly between 14:00:00 to 14:30:00 (or 2 PM to 2:30 PM), system clock time. - `secondary_frequency` (List of String) Specifies the second frequency that will be used for 80+80MHz configuration. Set it to `disabled` in order to disable 80+80MHz capability. - `skip_dfs_channels` (String) An option to avoid using channels on which channel availability check (listening for the presence of radar signals) is required. - `width` (String) Channel width. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wifi/channel get [print show-ids]] terraform import routeros_wifi_channel.channel1 '*1' ``` ================================================ FILE: docs/resources/wifi_configuration.md ================================================ # routeros_wifi_configuration (Resource) *This resource requires a minimum version of RouterOS 7.13.* ## Example Usage ```terraform resource "routeros_wifi_aaa" "aaa1" { called_format = "S" name = "aaa1" password_format = "" username_format = "AA:AA:AA:AA:AA:AA" } resource "routeros_wifi_channel" "channel1" { name = "1" band = "2ghz-n" frequency = [2412] secondary_frequency = ["disabled"] skip_dfs_channels = "disabled" width = "20mhz" } resource "routeros_wifi_datapath" "datapath1" { name = "datapath1" bridge = "bridge1" client_isolation = false } resource "routeros_wifi_security" "security1" { name = "security1" authentication_types = ["wpa2-psk", "wpa3-psk"] ft = true ft_preserve_vlanid = true passphrase = "password" } resource "routeros_wifi_configuration" "configuration1" { country = "Netherlands" manager = "capsman" mode = "ap" name = "configuration1" ssid = "my-network" aaa = { config = routeros_wifi_aaa.aaa1.name } channel = { config = routeros_wifi_channel.channel1.name } datapath = { config = routeros_wifi_datapath.datapath1.name } security = { config = routeros_wifi_security.security1.name } } ``` ## Schema ### Required - `name` (String) Name of the configuration. ### Optional - `aaa` (Map of String) AAA inline settings. - `antenna_gain` (Number) An option overrides the default antenna gain. - `beacon_interval` (String) Time interval between beacon frames. - `chains` (Set of Number) Radio chains to use for receiving signals. - `channel` (Map of String) Channel inline settings. - `comment` (String) - `country` (String) An option determines which regulatory domain restrictions are applied to an interface. - `datapath` (Map of String) Datapath inline settings. - `deprioritize_unii_3_4` (Boolean) Whether to assign lower priority to channels with a control frequency of 5720 or 5825-5885 MHz. These channels are unsupported by some client devices, making their automatic selection undesirable. Defaults to `yes` in ETSI regulatory domains, elsewhere to `no`. - `disabled` (Boolean) - `dtim_period` (Number) A period at which to transmit multicast traffic, when there are client devices in power save mode connected to the AP. - `hide_ssid` (Boolean) This property has effect only in AP mode. Setting it to yes can remove this network from the list of wireless networks that are shown by some client software. Changing this setting does not improve the security of the wireless network, because SSID is included in other frames sent by the AP. - `interworking` (Map of String) Interworking inline settings. - `manager` (String) An option to specify the remote CAP mode. - `mode` (String) An option to specify the access point operational mode. - `multicast_enhance` (String) An option to enable converting every multicast-address IP or IPv6 packet into multiple unicast-addresses frames for each connected station. - `qos_classifier` (String) An option to specify the QoS classifier. - `security` (Map of String) Security inline settings. - `ssid` (String) SSID (service set identifier) is a name broadcast in the beacons that identifies wireless network. - `steering` (Map of String) Steering inline settings. - `tx_chains` (Set of Number) Radio chains to use for transmitting signals. - `tx_power` (Number) A limit on the transmit power (in dBm) of the interface. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wifi/configuration get [print show-ids]] terraform import routeros_wifi_configuration.configuration1 '*1' #Or you can import a resource using one of its attributes terraform import routeros_wifi_configuration.configuration1 "name=xxx" ``` ================================================ FILE: docs/resources/wifi_datapath.md ================================================ # routeros_wifi_datapath (Resource) *This resource requires a minimum version of RouterOS 7.13.* ## Example Usage ```terraform resource "routeros_wifi_datapath" "datapath1" { name = "datapath1" bridge = "bridge1" client_isolation = false } ``` ## Schema ### Required - `name` (String) Name of the datapath. ### Optional - `bridge` (String) Bridge interface to add the interface as a bridge port. - `bridge_cost` (String) Spanning tree protocol cost of the bridge port. - `bridge_horizon` (String) Bridge horizon to use when adding as a bridge port. - `client_isolation` (Boolean) An option to toggle communication between clients connected to the same AP. - `comment` (String) - `disabled` (Boolean) - `interface_list` (String) List to which add the interface as a member. - `traffic_processing` (String) - `vlan_id` (Number) Default VLAN ID to assign to client devices connecting to this interface. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wifi/datapath get [print show-ids]] terraform import routeros_wifi_datapath.datapath1 '*1' ``` ================================================ FILE: docs/resources/wifi_interworking.md ================================================ # routeros_wifi_interworking (Resource) *This resource requires a minimum version of RouterOS 7.13.* ## Example Usage ```terraform resource "routeros_wifi_interworking" "interworking1" { name = "interworking1" internet = true } ``` ## Schema ### Required - `name` (String) Name of the interworking profile. ### Optional - `3gpp_info` (List of String) Cellular network advertisement information - country and network codes. - `3gpp_raw` (String) Cellular network advertisement information - country and network codes. - `asra` (Boolean) An option to enable Additional Steps Required for Access. - `authentication_types` (List of String) A list of authentication types that is only effective when `asra` is set to yes. - `comment` (String) - `connection_capabilities` (List of String) A list to provide information about the allowed IP protocols and ports. - `disabled` (Boolean) - `domain_names` (List of String) A list of fully qualified domain names (FQDN) that indicate the entity operating the Hotspot. - `esr` (Boolean) An option to enable Emergency Services Reachability. - `hessid` (String) Homogenous extended service set identifier (HESSID). - `hotspot20` (Boolean) An option to indicate Hotspot 2.0 capability of the Access Point. - `hotspot20_dgaf` (Boolean) An option to indicate Downstream Group-Addressed Forwarding (DGAF) capability. - `internet` (Boolean) An option to indicate Internet availability. - `ipv4_availability` (String) An option to indicate IPv4 availability. - `ipv6_availability` (String) An option to indicate IPv6 availability. - `network_type` (String) Information about network access type. - `operational_classes` (List of Number) A list with information about other available bands. - `operator_names` (List of String) A list of colon-separated operator names and language codes. - `realms` (List of String) A list of colon-separated realm names and EAP methods. - `realms_raw` (List of String) A list of 'NAI Realm Tuple' excluding 'NAI Realm Data Field Length' field. - `roaming_ois` (List of String) A list of Organization Identifiers (OI). - `uesa` (Boolean) An option to enable Unauthenticated Emergency Service Accessibility. - `venue` (String) Information about the venue in which the Access Point is located. - `venue_names` (List of String) A list of colon-separated venue names and language codes. - `wan_at_capacity` (Boolean) An option to indicate that the Access Point or the network is at its max capacity. - `wan_downlink` (Number) The downlink speed of the WAN connection set in kbps. - `wan_downlink_load` (Number) The downlink load of the WAN connection measured over `wan_measurement_duration`. - `wan_measurement_duration` (Number) The duration during which `wan_downlink_load` and `wan_uplink_load` are measured. - `wan_status` (String) Information about the status of the Access Point's WAN connection. - `wan_symmetric` (Boolean) An option to indicate that the WAN link is symmetric (upload and download speeds are the same). - `wan_uplink` (Number) The uplink speed of the WAN connection set in kbps. - `wan_uplink_load` (Number) The uplink load of the WAN connection measured over `wan_measurement_duration`. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wifi/interworking get [print show-ids]] terraform import routeros_wifi_interworking.interworking1 '*1' #Or you can import a resource using one of its attributes terraform import routeros_wifi_interworking.interworking1 "name=xxx" ``` ================================================ FILE: docs/resources/wifi_provisioning.md ================================================ # routeros_wifi_provisioning (Resource) *This resource requires a minimum version of RouterOS 7.13.* ## Example Usage ```terraform resource "routeros_wifi_configuration" "configuration1" { country = "Netherlands" manager = "capsman" mode = "ap" name = "configuration1" ssid = "my-network" } resource "routeros_wifi_provisioning" "provisioning1" { action = "create-enabled" master_configuration = routeros_wifi_configuration.configuration1.name name_format = "cap1:" radio_mac = "00:11:22:33:44:55" } ``` ## Schema ### Optional - `action` (String) Provisioning action. - `address_ranges` (List of String) Match CAPs by IPs within configured address ranges. - `comment` (String) - `common_name_regexp` (String) Regular expression to match radios by common name. - `disabled` (Boolean) - `identity_regexp` (String) Regular expression to match radios by router identity. - `master_configuration` (String) If action specifies to create interfaces, then a new master interface with its configuration set to this configuration profile will be created. - `name_format` (String) Specify the format of the CAP interface name creation. - `radio_mac` (String) MAC address of radio to be matched, empty MAC means match all MAC addresses. `00:00:00:00:00:00` is not considered empty MAC-address. - `slave_configurations` (List of String) If action specifies to create interfaces, then a new slave interface for each configuration profile in this list is created. - `slave_name_format` (String) The name format of the slave CAP interfaces. This option is available in RouterOS starting from version 7.16. - `supported_bands` (List of String) Match CAPs by supported modes. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wifi/provisioning get [print show-ids]] terraform import routeros_wifi_provisioning.provisioning1 '*1' #Or you can import a resource using one of its attributes terraform import routeros_wifi_provisioning.provisioning1 "name=xxx" ``` ================================================ FILE: docs/resources/wifi_security.md ================================================ # routeros_wifi_security (Resource) *This resource requires a minimum version of RouterOS 7.13.* ## Example Usage ```terraform resource "routeros_wifi_security" "security1" { name = "security1" authentication_types = ["wpa2-psk", "wpa3-psk"] ft = true ft_preserve_vlanid = true passphrase = "password" } ``` ## Schema ### Required - `name` (String) Name of the security profile. ### Optional - `authentication_types` (Set of String) Authentication types to enable on the interface. - `comment` (String) - `connect_group` (String) APs within the same connect group do not allow more than 1 client device with the same MAC address. - `connect_priority` (String) An option to determine how a connection is handled if the MAC address of the client device is the same as that of another active connection to another AP. - `dh_groups` (Set of Number) Identifiers of elliptic curve cryptography groups to use in SAE (WPA3) authentication. - `disable_pmkid` (Boolean) An option to disable inclusion of a PMKID in EAPOL frames. - `disabled` (Boolean) - `eap_accounting` (Boolean) An option to send accounting information to RADIUS server for EAP-authenticated peers. - `eap_anonymous_identity` (String) An option to specify anonymous identity for EAP outer authentication. - `eap_certificate_mode` (String) A policy for handling the TLS certificate of the RADIUS server. - `eap_methods` (Set of String) A set of EAP methods to consider for authentication. - `eap_password` (String) Password to use when the chosen EAP method requires one. - `eap_tls_certificate` (String) Name or id of a certificate in the device's certificate store to use when the chosen EAP authentication method requires one. - `eap_username` (String) Username to use when the chosen EAP method requires one. - `encryption` (Set of String) A list of ciphers to support for encrypting unicast traffic. - `ft` (Boolean) An option to enable 802.11r fast BSS transitions (roaming). - `ft_mobility_domain` (String) The fast BSS transition mobility domain ID. - `ft_nas_identifier` (String) Fast BSS transition PMK-R0 key holder identifier. - `ft_over_ds` (Boolean) An option to enable fast BSS transitions over DS (distributed system). - `ft_preserve_vlanid` (Boolean) An option to preserve VLAN ID when roaming. - `ft_r0_key_lifetime` (String) The lifetime of the fast BSS transition PMK-R0 encryption key. - `ft_reassociation_deadline` (String) Fast BSS transition reassociation deadline. - `group_encryption` (String) A cipher to use for encrypting multicast traffic. - `group_key_update` (String) The interval at which the group temporal key (key for encrypting broadcast traffic) is renewed. - `management_encryption` (String) A cipher to use for encrypting protected management frames. - `management_protection` (String) An option to enable 802.11w management frame protection. - `multi_passphrase_group` (String) Name of `/interface/wifi/security/multi-passphrase/` group that will be used. Only a single group can be defined under the security profile. - `owe_transition_interface` (String) Name or internal ID of an interface which MAC address and SSID to advertise as the matching AP when running in OWE transition mode. - `passphrase` (String) Passphrase to use for PSK authentication types. - `sae_anti_clogging_threshold` (String) A parameter to mitigate DoS attacks by specifying a threshold of in-progress SAE authentications. - `sae_max_failure_rate` (String) Rate of failed SAE (WPA3) associations per minute, at which the AP will stop processing new association requests. - `sae_pwe` (String) Methods to support for deriving SAE password element. - `wps` (String) An option to enable WPS (Wi-Fi Protected Setup). ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wifi/security get [print show-ids]] terraform import routeros_wifi_security.security1 '*1' #Or you can import a resource using one of its attributes terraform import routeros_wifi_security.security1 "name=xxx" ``` ================================================ FILE: docs/resources/wifi_security_multi_passphrase.md ================================================ # routeros_wifi_security_multi_passphrase (Resource) *This resource requires a minimum version of RouterOS 7.17beta1.* ## Example Usage ```terraform resource "routeros_wifi_security_multi_passphrase" "test" { group = "gr-123" passphrase = data.vault_kv_secret_v2.wifi_security.data["test"] } ``` ## Schema ### Required - `group` (String) Assigning the group to a security profile or an access list, will enable use of all passphrases defined under it. ### Optional - `comment` (String) - `disabled` (Boolean) - `expires` (String) The expiration date and time for passphrase specified in this entry, doesn't affect the whole group. Once the date is reached, existing clients using this passphrase will be disconnected, and new clients will not be able to connect using it. If not set, passphrase can be used indefinetly. - `isolation` (Boolean) Determines whether the client device using this passphrase is isolated from other clients on AP. Traffic from an isolated client will not be forwarded to other clients and unicast traffic from a non-isolated client will not be forwarded to an isolated one. - `passphrase` (String, Sensitive) The passphrase to use for PSK authentication types. Multiple users can use the same passphrase. Not compatible with WPA3-PSK. - `vlan_id` (Number) vlan-id that will be assigned to clients using this passphrase Only supported on wifi-qcom interfaces, if wifi-qcom-ac AP has a client that uses a passphrase that has vlan-id associated with it, the client will not be able to join. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/wifi/security/multi/passphrase get [print show-ids]] terraform import routeros_wifi_security_multi_passphrase.test *3 #Or you can import a resource using one of its attributes terraform import routeros_wifi_security_multi_passphrase.test "comment=xxx" ``` ================================================ FILE: docs/resources/wifi_steering.md ================================================ # routeros_wifi_steering (Resource) *This resource requires a minimum version of RouterOS 7.13.* ## Example Usage ```terraform resource "routeros_wifi_steering" "steering1" { name = "steering1" neighbor_group = ["something"] } ``` ## Schema ### Required - `name` (String) Name of the steering profile. ### Optional - `comment` (String) - `disabled` (Boolean) - `neighbor_group` (List of String) Neighbor group of potential roaming candidates. - `rrm` (Boolean) An option to enable sending 802.11k neighbor reports. - `wnm` (Boolean) An option to enable sending 802.11v BSS transition management requests. ### Read-Only - `id` (String) The ID of this resource. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wifi/steering get [print show-ids]] terraform import routeros_wifi_steering.steering1 '*1' #Or you can import a resource using one of its attributes terraform import routeros_wifi_steering.steering1 "name=xxx" ``` ================================================ FILE: docs/resources/wireguard.md ================================================ # routeros_wireguard (Resource) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_interface_wireguard](interface_wireguard.md) ================================================ FILE: docs/resources/wireguard_keys.md ================================================ # routeros_wireguard_keys (Resource) Creating key sets for WireGuard tunnels. ## Example Usage ```terraform resource "routeros_wireguard_keys" "wgk" { number = 3 } output "wg_keys" { value = routeros_wireguard_keys.wgk.keys[*] sensitive = true } output "wg_key" { value = nonsensitive(routeros_wireguard_keys.wgk.keys[1].public) } ``` ## Schema ### Optional - `number` (Number) The number of key sets. ### Read-Only - `id` (String) The ID of this resource. - `keys` (List of Object, Sensitive) (see [below for nested schema](#nestedatt--keys)) ### Nested Schema for `keys` Read-Only: - `preshared` (String) - `private` (String) - `public` (String) ================================================ FILE: docs/resources/wireguard_peer.md ================================================ # routeros_wireguard_peer (Resource) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_interface_wireguard_peer](interface_wireguard_peer.md) ================================================ FILE: docs/resources/zerotier.md ================================================ # routeros_zerotier (Resource) ## Example Usage ```terraform resource "zerotier_identity" "identity" {} resource "routeros_zerotier" "zt1" { comment = "ZeroTier Central" identity = zerotier_identity.identity.private_key interfaces = ["all"] name = "zt1" } ``` ## Schema ### Required - `name` (String) Name of the ZeroTier instance. ### Optional - `comment` (String) - `disabled` (Boolean) - `identity` (String) The 40-bit unique instance address. - `interfaces` (Set of String) The interfaces to discover ZeroTier peers by ARP and IP type connections. - `port` (Number) The port number the instance listens to. - `route_distance` (Number) The route distance for routes obtained from the planet/moon server. ### Read-Only - `id` (String) The ID of this resource. - `identity_public` (String) The public identity of the ZeroTier instance. - `online` (Boolean) A flag whether the ZeroTier instance is currently online. - `state` (String) The state of the ZeroTier instance. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/zerotier get [print show-ids]] terraform import routeros_zerotier.zt1 '*1' #Or you can import a resource using one of its attributes terraform import routeros_zerotier.zt1 "name=xxx" ``` ================================================ FILE: docs/resources/zerotier_controller.md ================================================ # routeros_zerotier_controller (Resource) ## Example Usage ```terraform resource "zerotier_identity" "identity" {} resource "routeros_zerotier" "zt1" { comment = "ZeroTier Central" identity = zerotier_identity.identity.private_key interfaces = ["all"] name = "zt1" } resource "routeros_zerotier_controller" "test" { instance = routeros_zerotier.zt1.name name = "test" network = "1234567812345678" private = true } ``` ## Schema ### Required - `instance` (String) The ZeroTier instance name. - `name` (String) Name of the ZeroTier controller. - `network` (String) The ZeroTier network identifier. ### Optional - `broadcast` (Boolean) An option to allow receiving broadcast packets. - `comment` (String) - `disabled` (Boolean) - `ip6_6plane` (Boolean) An option to assign every member a `/80` address within a `/40` network with using NDP emulation. - `ip6_range` (String) The IPv6 range of the ZeroTier network. - `ip6_rfc4193` (Boolean) An option to assign every member a `/128` address within a `/88` network. - `ip_range` (String) The IP range of the ZeroTier network. - `mtu` (Number) Layer2 Maximum transmission unit. [See](https://wiki.mikrotik.com/wiki/Maximum_Transmission_Unit_on_RouterBoards). - `multicast_limit` (Number) An option to limit the maximum recipients of a multicast packet. - `private` (Boolean) The ZeroTier network access control. - `routes` (Set of String) The routes list that will be pushed to the client. ### Read-Only - `id` (String) The ID of this resource. - `inactive` (Boolean) A flag whether the ZeroTier network is inactive. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/zerotier/controller get [print show-ids]] terraform import routeros_zerotier_controller.test '*1' #Or you can import a resource using one of its attributes terraform import routeros_zerotier_controller.test "name=xxx" ``` ================================================ FILE: docs/resources/zerotier_interface.md ================================================ # routeros_zerotier_interface (Resource) ## Example Usage ```terraform resource "zerotier_identity" "identity" {} resource "zerotier_network" "network" { name = "test" } resource "zerotier_member" "member" { authorized = true member_id = zerotier_identity.identity.id name = "test" network_id = zerotier_network.network.id hidden = false allow_ethernet_bridging = true no_auto_assign_ips = true } resource "routeros_zerotier" "zt1" { comment = "ZeroTier Central" identity = zerotier_identity.identity.private_key interfaces = ["all"] name = "zt1" } resource "routeros_zerotier_interface" "zerotier1" { allow_default = false allow_global = false allow_managed = false instance = routeros_zerotier.zt1.name name = "zerotier1" network = zerotier_network.network.id } ``` ## Schema ### Required - `instance` (String) The ZeroTier instance name. - `name` (String) Name of the ZeroTier interface. - `network` (String) The ZeroTier network identifier. ### Optional - `allow_default` (Boolean) An option to override the default route. - `allow_global` (Boolean) An option to allow overlapping public IP space by the ZeroTier routes. . - `allow_managed` (Boolean) An option to allow assignment of managed IPs. - `arp_timeout` (String) ARP timeout is time how long ARP record is kept in ARP table after no packets are received from IP. Value auto equals to the value of arp-timeout in IP/Settings, default is 30s. Can use postfix `ms`, `s`, `m`, `h`, `d` for milliseconds, seconds, minutes, hours or days. If no postfix is set then seconds (s) is used. - `comment` (String) - `disable_running_check` (Boolean) An option to force the `running` property to true. - `disabled` (Boolean) ### Read-Only - `bridge` (Boolean) A flag whether the ZeroTier interface is bridged. - `dhcp` (Boolean) A flag whether the ZeroTier interface obtained an IP address. - `id` (String) The ID of this resource. - `mac_address` (String) Current mac address. - `mtu` (Number) Layer2 Maximum transmission unit. [See](https://wiki.mikrotik.com/wiki/Maximum_Transmission_Unit_on_RouterBoards). - `network_name` (String) The ZeroTier network name. - `running` (Boolean) - `status` (String) The status of the ZeroTier connection. - `type` (String) The ZeroTier network type. ## Import Import is supported using the following syntax: ```shell #The ID can be found via API or the terminal #The command for the terminal is -> :put [/zerotier/interface get [print show-ids]] terraform import routeros_zerotier_interface.zerotier1 '*1' #Or you can import a resource using one of its attributes terraform import routeros_zerotier_interface.zerotier1 "name=xxx" ``` ================================================ FILE: examples/data-sources/routeros_files/data-source.tf ================================================ data "routeros_files" "files" {} ================================================ FILE: examples/data-sources/routeros_interface_bridge_filter/data-source.tf ================================================ data "routeros_interface_bridge_filter" "rules" {} ================================================ FILE: examples/data-sources/routeros_interfaces/data-source.tf ================================================ data "routeros_interfaces" "interfaces" {} ================================================ FILE: examples/data-sources/routeros_ip_addresses/data-source.tf ================================================ data "routeros_ip_addresses" "ip_addresses" {} ================================================ FILE: examples/data-sources/routeros_ip_arp/data-source.tf ================================================ data "routeros_ip_arp" "data" {} ================================================ FILE: examples/data-sources/routeros_ip_dhcp_server_leases/data-source.tf ================================================ data "routeros_ip_dhcp_server_leases" "data" {} ================================================ FILE: examples/data-sources/routeros_ip_firewall/data-source.tf ================================================ data "routeros_ip_firewall" "fw" { rules { filter = { chain = "input" comment = "rule_2" } } rules { filter = { chain = "forward" } } nat {} } output "rules" { value = [for value in data.routeros_ip_firewall.fw.rules : [value.id, value.src_address]] } output "nat" { value = [for value in data.routeros_ip_firewall.fw.nat : [value.id, value.comment]] } resource "routeros_ip_firewall" "rule_3" { action = "accept" chain = "input" comment = "rule_3" src_address = "192.168.0.5" place_before = data.routeros_ip_firewall_filter.fw.rules[0].id } ================================================ FILE: examples/data-sources/routeros_ip_routes/data-source.tf ================================================ data "routeros_ip_routes" "ip_routes" {} ================================================ FILE: examples/data-sources/routeros_ip_services/data-source.tf ================================================ data "routeros_ip_services" "router" { provider = routeros.router } resource "routeros_ip_service" "router-disabled" { provider = routeros.router for_each = { for s in data.routeros_ip_services.router.services : s.name => s if s.name != "www-ssl" } disabled = true numbers = each.value.name port = each.value.port } ================================================ FILE: examples/data-sources/routeros_ipv6_addresses/data-source.tf ================================================ data "routeros_ipv6_addresses" "addresses" {} ================================================ FILE: examples/data-sources/routeros_system_resource/data-source.tf ================================================ data "routeros_system_resource" "data" {} ================================================ FILE: examples/data-sources/routeros_system_routerboard/data-source.tf ================================================ data "routeros_system_routerboard" "data" {} ================================================ FILE: examples/data-sources/routeros_wifi_easy_connect/data-source.tf ================================================ data "routeros_wifi_easy_connect" "test" { type = "WPA2" ssid = "test" password = "password12345" } output "qrcode" { value = data.routeros_wifi_easy_connect.test.qr_code } # We can disable the QR code output and view it in the state file if needed. # terraform.exe state show data.routeros_wifi_easy_connect.test ================================================ FILE: examples/data-sources/routeros_x509/data-source.tf ================================================ # You can keep indents in front of the content lines of the certificate. # The normalized certificate is available through the `pem` attribute data "routeros_x509" "cert" { data = < :put [/caps-man/access-list get [print show-ids]] terraform import routeros_capsman_access_list.test_rule "*1" #Or you can import a resource using one of its attributes terraform import routeros_capsman_access_list.test_rule "name=xxx" ================================================ FILE: examples/resources/routeros_capsman_access_list/resource.tf ================================================ resource "routeros_capsman_datapath" "test_rule" { comment = "Catch-all" interface = "cap1" signal_range = "-120..-85" allow_signal_out_of_range = "20s" action = "reject" } ================================================ FILE: examples/resources/routeros_capsman_channel/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/caps-man/channel get [print show-ids]] terraform import routeros_capsman_channel.test_channel "*1" #Or you can import a resource using one of its attributes terraform import routeros_capsman_channel.test_channel "name=xxx" ================================================ FILE: examples/resources/routeros_capsman_channel/resource.tf ================================================ resource "routeros_capsman_channel" "test_channel" { name = "test_channel" comment = "test_channel" band = "2ghz-b/g/n" control_channel_width = "10mhz" extension_channel = "eCee" frequency = [2412] reselect_interval = "1h" save_selected = true secondary_frequency = ["disabled"] skip_dfs_channels = true tx_power = 20 } ================================================ FILE: examples/resources/routeros_capsman_configuration/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/caps-man/configuration get [print show-ids]] terraform import routeros_capsman_configuration.test_configuration_2 "*1" #Or you can import a resource using one of its attributes terraform import routeros_capsman_configuration.test_configuration_2 "name=xxx" ================================================ FILE: examples/resources/routeros_capsman_configuration/resource.tf ================================================ resource "routeros_capsman_configuration" "test_configuration" { comment = "Comment" country = "no_country_set" disconnect_timeout = "1s150ms" distance = "indoors" frame_lifetime = "0.12" // 120ms guard_interval = "long" hide_ssid = true hw_protection_mode = "rts-cts" hw_retries = 1 installation = "indoor" keepalive_frames = "enabled" load_balancing_group = "" max_sta_count = 1 mode = "ap" multicast_helper = "full" name = "test_configuration" rx_chains = [1, 3] ssid = "SSID" tx_chains = [0, 2] } resource "routeros_capsman_channel" "test_channel" { name = "test-channel-config" } resource "routeros_capsman_datapath" "test_datapath" { name = "test-datapath-config" } resource "routeros_capsman_rates" "test_rates" { name = "test-rates-config" } resource "routeros_capsman_security" "test_security" { name = "test-security-config" } resource "routeros_capsman_configuration" "test_configuration_2" { name = "test_configuration_name" channel = { config = "${routeros_capsman_channel.test_channel.name}" band = "2ghz-b/g/n" control_channel_width = "10mhz" extension_channel = "eCee" frequency = 2412 reselect_interval = "1h" save_selected = "true" secondary_frequency = "disabled" skip_dfs_channels = "true" tx_power = 20 } datapath = { config = "${routeros_capsman_datapath.test_datapath.name}" arp = "local-proxy-arp" bridge = "bridge" bridge_cost = "100" bridge_horizon = "200" client_to_client_forwarding = "true" interface_list = "static" l2mtu = "1450" local_forwarding = "true" mtu = "1500" vlan_id = "101" vlan_mode = "no-tag" // openflow_switch = "aaa" } rates = { config = "${routeros_capsman_rates.test_rates.name}" basic = "1Mbps,5.5Mbps,6Mbps,18Mbps,36Mbps,54Mbps" ht_basic_mcs = "mcs-0,mcs-7,mcs-11,mcs-14,mcs-16,mcs-21" ht_supported_mcs = "mcs-3,mcs-8,mcs-10,mcs-13,mcs-17,mcs-18" supported = "2Mbps,11Mbps,9Mbps,12Mbps,24Mbps,48Mbps" vht_basic_mcs = "none" vht_supported_mcs = "mcs0-9,mcs0-7" } security = { config = "${routeros_capsman_security.test_security.name}" authentication_types = "wpa-psk,wpa-eap" disable_pmkid = "true" eap_methods = "eap-tls,passthrough" eap_radius_accounting = "true" encryption = "aes-ccm,tkip" group_encryption = "aes-ccm" group_key_update = "1h" passphrase = "AAAAAAAAA" tls_certificate = "none" tls_mode = "verify-certificate" } depends_on = [ routeros_capsman_channel.test_channel, routeros_capsman_datapath.test_datapath, routeros_capsman_rates.test_rates, routeros_capsman_security.test_security ] } ================================================ FILE: examples/resources/routeros_capsman_datapath/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/caps-man/datapath get [print show-ids]] terraform import routeros_capsman_datapath.test_datapath "*1" #Or you can import a resource using one of its attributes terraform import routeros_capsman_datapath.test_datapath "name=xxx" ================================================ FILE: examples/resources/routeros_capsman_datapath/resource.tf ================================================ resource "routeros_capsman_datapath" "test_datapath" { name = "test_datapath" comment = "test_datapath" arp = "local-proxy-arp" bridge = "bridge" bridge_cost = 100 bridge_horizon = 200 client_to_client_forwarding = true interface_list = "static" l2mtu = 1450 local_forwarding = true mtu = 1500 vlan_id = 101 vlan_mode = "no-tag" // openflow_switch = "aaa" } ================================================ FILE: examples/resources/routeros_capsman_interface/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/caps-man/interface get [print show-ids]] terraform import routeros_capsman_interface.cap1 '*1' ================================================ FILE: examples/resources/routeros_capsman_interface/resource.tf ================================================ resource "routeros_capsman_channel" "channel1" { name = "1" band = "2ghz-g/n" frequency = [2412] } resource "routeros_capsman_interface" "cap1" { name = "cap1" channel = { config = routeros_capsman_channel.channel1.name } } ================================================ FILE: examples/resources/routeros_capsman_manager/import.sh ================================================ terraform import routeros_capsman_manager.test_manager . ================================================ FILE: examples/resources/routeros_capsman_manager/resource.tf ================================================ resource "routeros_capsman_manager" "test_manager" { enabled = true upgrade_policy = "require-same-version" } ================================================ FILE: examples/resources/routeros_capsman_manager_interface/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/caps-man/manager/interface get [print show-ids]] terraform import routeros_capsman_manager_interface.test_manager_interface "*6" ================================================ FILE: examples/resources/routeros_capsman_manager_interface/resource.tf ================================================ resource "routeros_capsman_manager_interface" "test_manager_interface" { interface = "ether1" forbid = true } ================================================ FILE: examples/resources/routeros_capsman_provisioning/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/caps-man/provisioning get [print show-ids]] terraform import routeros_capsman_provisioning.test_provisioning "*B" #Or you can import a resource using one of its attributes terraform import routeros_capsman_provisioning.test_provisioning "name=xxx" ================================================ FILE: examples/resources/routeros_capsman_provisioning/resource.tf ================================================ resource "routeros_capsman_configuration" "test_configuration" { name = "cfg1" } resource "routeros_capsman_provisioning" "test_provisioning" { master_configuration = "cfg1" action = "create-disabled" name_prefix = "cap-" depends_on = [ routeros_capsman_configuration.test_configuration, ] } ================================================ FILE: examples/resources/routeros_capsman_rates/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/caps-man/rates get [print show-ids]] terraform import routeros_capsman_rates.test_rates "*1" #Or you can import a resource using one of its attributes terraform import routeros_capsman_rates.test_rates "name=xxx" ================================================ FILE: examples/resources/routeros_capsman_rates/resource.tf ================================================ resource "routeros_capsman_rates" "test_rates" { name = "test_rates" comment = "test_rates" basic = ["1Mbps", "5.5Mbps", "6Mbps", "18Mbps", "36Mbps", "54Mbps"] ht_basic_mcs = ["mcs-0", "mcs-7", "mcs-11", "mcs-14", "mcs-16", "mcs-21"] ht_supported_mcs = ["mcs-3", "mcs-8", "mcs-10", "mcs-13", "mcs-17", "mcs-18"] supported = ["2Mbps", "11Mbps", "9Mbps", "12Mbps", "24Mbps", "48Mbps"] vht_basic_mcs = "none" vht_supported_mcs = "mcs0-9,mcs0-7" } ================================================ FILE: examples/resources/routeros_capsman_security/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/caps-man/security get [print show-ids]] terraform import routeros_capsman_security.test_security "*1" #Or you can import a resource using one of its attributes terraform import routeros_capsman_security.test_security "name=xxx" ================================================ FILE: examples/resources/routeros_capsman_security/resource.tf ================================================ resource "routeros_capsman_security" "test_security" { name = "test_security" comment = "test_security" authentication_types = ["wpa-psk", "wpa-eap", "wpa2-psk"] disable_pmkid = true eap_methods = "eap-tls,passthrough" eap_radius_accounting = true encryption = ["tkip", "aes-ccm"] group_encryption = "aes-ccm" group_key_update = "1h" passphrase = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE" tls_certificate = "none" tls_mode = "verify-certificate" } ================================================ FILE: examples/resources/routeros_container/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/container get [print show-ids]] terraform import routeros_container.busybox "*1" ================================================ FILE: examples/resources/routeros_container/resource.tf ================================================ resource "routeros_container" "busybox" { remote_image = "library/busybox:1.35.0" cmd = "/bin/httpd -f -p 8080" interface = routeros_interface_veth.busybox.name logging = true root_dir = "/usb1-part1/containers/busybox/root" start_on_boot = true } ================================================ FILE: examples/resources/routeros_container_config/import.sh ================================================ terraform import routeros_container_config.config . ================================================ FILE: examples/resources/routeros_container_config/resource.tf ================================================ resource "routeros_container_config" "config" { registry_url = "https://registry-1.docker.io" ram_high = "0" tmpdir = "/usb1-part1/containers/tmp" layer_dir = "/usb1-part1/containers/layers" } ================================================ FILE: examples/resources/routeros_container_envs/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/container/envs get [print show-ids]] terraform import routeros_container_envs.test_envs "*1" #Or you can import a resource using one of its attributes terraform import routeros_container_envs.test_envs "name=xxx" ================================================ FILE: examples/resources/routeros_container_envs/resource.tf ================================================ resource "routeros_container_envs" "test_envs" { name = "test_envs" key = "TZ" value = "UTC" } ================================================ FILE: examples/resources/routeros_container_mounts/import.sh ================================================ # Import with the name of the container mount in case of the example use Caddyfile terraform import routeros_container_mounts.caddyfile Caddyfile #Or you can import a resource using one of its attributes terraform import routeros_container_mounts.caddyfile "name=xxx" ================================================ FILE: examples/resources/routeros_container_mounts/resource.tf ================================================ resource "routeros_container_mounts" "caddyfile" { name = "Caddyfile" src = "/usb1-part1/containers/caddy/Caddyfile" dst = "/etc/caddy/Caddyfile" } ================================================ FILE: examples/resources/routeros_disk_settings/import.sh ================================================ terraform import routeros_disk_settings.test . ================================================ FILE: examples/resources/routeros_disk_settings/resource.tf ================================================ resource "routeros_disk_settings" "test" { auto_smb_sharing = false auto_smb_user = "guest" auto_media_sharing = false auto_media_interface = "lo" } ================================================ FILE: examples/resources/routeros_file/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/file get [print show-ids]] terraform import routeros_file.test "*1" #Or you can import a resource using one of its attributes terraform import routeros_file.test "name=xxx" ================================================ FILE: examples/resources/routeros_file/resource.tf ================================================ resource "routeros_file" "test" { name = "test" contents = "This is a test" } ================================================ FILE: examples/resources/routeros_interface_6to4/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/6to4 get [print show-ids]] terraform import routeros_interface_6to4.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_6to4.test "name=6to4-tunnel1" ================================================ FILE: examples/resources/routeros_interface_6to4/resource.tf ================================================ resource "routeros_interface_6to4" "test" { name = "6to4-tunnel1" keepalive = "10,10" } ================================================ FILE: examples/resources/routeros_interface_bonding/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/bonding get [print show-ids]] terraform import routeros_interface_bonding.test "*0" ================================================ FILE: examples/resources/routeros_interface_bonding/resource.tf ================================================ resource "routeros_interface_bonding" "test" { name = "bonding-test" slaves = ["ether3", "ether4"] } ================================================ FILE: examples/resources/routeros_interface_bridge/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/bridge get [print show-ids]] terraform import routeros_interface_bridge.bridge "*1" ================================================ FILE: examples/resources/routeros_interface_bridge/resource.tf ================================================ resource "routeros_interface_bridge" "bridge" { name = "bridge" vlan_filtering = true } ================================================ FILE: examples/resources/routeros_interface_bridge_filter/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/bridge/filter get [print show-ids]] terraform import routeros_interface_bridge_filter.rule "*0" #Or you can import a resource using one of its attributes terraform import routeros_interface_bridge_filter.rule "dst_address=224.0.0.251/32" ================================================ FILE: examples/resources/routeros_interface_bridge_filter/resource.tf ================================================ variable "bridge_filter_rule" { type = list(object({ chain = string action = string connection_state = optional(string) in_interface_list = optional(string, "all") out_interface_list = optional(string) src_address = optional(string) dst_address = optional(string) src_port = optional(string) dst_port = optional(string) jump_target = optional(string) protocol = optional(string) comment = optional(string, "(terraform-defined)") log = optional(bool, false) log_prefix = optional(string, "") disabled = optional(bool, false) })) default = [ { "action" = "drop", "chain" = "forward", "comment" = "Drop data between bridge ports" }, { "action" = "drop", "chain" = "forward", "comment" = "Block VLAN encap", "log_prefix" = "Block VLAN encap", "mac_protocol" = "vlan" }, { "action" = "accept", "chain" = "forward", "comment" = "", "disabled" = "true", "dst_address" = "224.0.0.251/32", "ip_protocol" = "udp", "log_prefix" = "Allow bonjour", "mac_protocol" = "ip" }, ] } locals { rule_map = { for idx, rule in var.bridge_filter_rule : format("%03d", idx) => rule } } resource "routeros_interface_bridge_filter" "rules" { for_each = local.rule_map chain = each.value.chain action = each.value.action comment = each.value.comment log = each.value.log log_prefix = each.value.log_prefix disabled = each.value.disabled connection_state = each.value.connection_state in_interface_list = each.value.in_interface_list dst_port = each.value.dst_port protocol = each.value.protocol src_address = each.value.src_address jump_target = each.value.jump_target } resource "routeros_move_items" "bridge_filter_rules" { # resource_name = "routeros_interface_bridge_filter" resource_path = "/interface/bridge/filter" sequence = [for i, _ in local.rule_map : routeros_interface_bridge_filter.rules[i].id] depends_on = [routeros_interface_bridge_filter.rules] } ================================================ FILE: examples/resources/routeros_interface_bridge_port/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/bridge/port get [print show-ids]] terraform import routeros_interface_bridge_port.bridge_port "*0" #Or you can import a resource using one of its attributes terraform import routeros_interface_bridge_port.bridge_port "name=xxx" ================================================ FILE: examples/resources/routeros_interface_bridge_port/resource.tf ================================================ resource "routeros_interface_bridge_port" "bridge_port" { bridge = "bridge" interface = "ether5" pvid = "50" } ================================================ FILE: examples/resources/routeros_interface_bridge_settings/import.sh ================================================ terraform import routeros_interface_bridge_settings.settings . ================================================ FILE: examples/resources/routeros_interface_bridge_settings/resource.tf ================================================ resource "routeros_interface_bridge_settings" "settings" { use_ip_firewall = true } ================================================ FILE: examples/resources/routeros_interface_bridge_vlan/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/bridge/vlan get [print show-ids]] terraform import routeros_interface_bridge_vlan.bridge_vlan "*0" #Or you can import a resource using one of its attributes terraform import routeros_interface_bridge_vlan.bridge_vlan "name=xxx" ================================================ FILE: examples/resources/routeros_interface_bridge_vlan/resource.tf ================================================ resource "routeros_interface_bridge_vlan" "bridge_vlan" { bridge = "bridge" vlan_ids = ["50"] tagged = [ "bridge", "ether1" ] untagged = [ "ether5" ] } resource "routeros_interface_bridge_vlan" "bridge_vlan" { bridge = "bridge" vlan_ids = ["4", "10", "20", "50", "100", "101", "102", "103", "112", "201", "202", "220", "254"] } resource "routeros_interface_bridge_vlan" "bridge_vlan" { bridge = "bridge" vlan_ids = ["4", "10", "20", "50", "100-103", "112", "201", "202", "220", "254"] } resource "routeros_interface_bridge_vlan" "bridge_vlan" { bridge = "bridge" vlan_ids = ["100-115", "120", "122", "128-130"] } ================================================ FILE: examples/resources/routeros_interface_detect_internet/import.sh ================================================ terraform import routeros_interface_detect_internet.test . ================================================ FILE: examples/resources/routeros_interface_detect_internet/resource.tf ================================================ resource "routeros_interface_detect_internet" "test" { internet_interface_list = "all" wan_interface_list = "all" lan_interface_list = "all" detect_interface_list = "all" } ================================================ FILE: examples/resources/routeros_interface_dot1x_client/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/dot1x/client get [print show-ids]] terraform import routeros_interface_dot1x_client.ether2 *1 #Or you can import a resource using one of its attributes terraform import routeros_interface_dot1x_client.ether2 "name=xxx" ================================================ FILE: examples/resources/routeros_interface_dot1x_client/resource.tf ================================================ resource "routeros_interface_dot1x_client" "ether2" { eap_methods = ["eap-peap", "eap-mschapv2"] identity = "router" interface = "ether2" } ================================================ FILE: examples/resources/routeros_interface_dot1x_server/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/dot1x/server get [print show-ids]] terraform import routeros_interface_dot1x_server.ether2 *1 #Or you can import a resource using one of its attributes terraform import routeros_interface_dot1x_server.ether2 "name=xxx" ================================================ FILE: examples/resources/routeros_interface_dot1x_server/resource.tf ================================================ resource "routeros_interface_dot1x_server" "ether2" { auth_types = ["mac-auth"] interface = "ether2" } ================================================ FILE: examples/resources/routeros_interface_eoip/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/eoip get [print show-ids]] terraform import routeros_interface_eoip.eoip_tunnel1 *B #Or you can import a resource using one of its attributes terraform import routeros_interface_eoip.eoip_tunnel1 "name=xxx" ================================================ FILE: examples/resources/routeros_interface_eoip/resource.tf ================================================ resource "routeros_interface_eoip" "eoip_tunnel1" { name = "eoip-tunnel1" local_address = "192.168.88.1" remote_address = "192.168.88.2" disabled = true } ================================================ FILE: examples/resources/routeros_interface_ethernet/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/ethernet get [print show-ids]] terraform import routeros_interface_ethernet.test "*0" #Or you can import a resource using one of its attributes terraform import routeros_interface_ethernet.test "name=xxx" ================================================ FILE: examples/resources/routeros_interface_ethernet/resource.tf ================================================ resource "routeros_interface_ethernet" "test" { factory_name = "sfp-sfpplus8" name = "swtich-eth0" mtu = 9000 } ================================================ FILE: examples/resources/routeros_interface_ethernet_switch/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/ethernet/switch get [print show-ids]] terraform import routeros_interface_ethernet_switch.sw0 *0 ================================================ FILE: examples/resources/routeros_interface_ethernet_switch/resource.tf ================================================ resource "routeros_interface_ethernet_switch" "sw0" { switch_id = 0 # Optional name = "new switch" } ================================================ FILE: examples/resources/routeros_interface_ethernet_switch_crs/import.sh ================================================ terraform import routeros_interface_ethernet_switch_crs.sw0 . ================================================ FILE: examples/resources/routeros_interface_ethernet_switch_crs/resource.tf ================================================ resource "routeros_interface_ethernet_switch_crs" "sw0" { switch_id = 0 name = "new switch" drop_if_invalid_or_src_port_not_member_of_vlan_on_ports = ["ether1", "ether2", "ether3"] } ================================================ FILE: examples/resources/routeros_interface_ethernet_switch_crs_egress_vlan_tag/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/ethernet/switch/egress-vlan-tag get [print show-ids]] terraform import routeros_interface_ethernet_switch_crs_egress_vlan_tag.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_ethernet_switch_crs_egress_vlan_tag.test "name=xxx" ================================================ FILE: examples/resources/routeros_interface_ethernet_switch_crs_egress_vlan_tag/resource.tf ================================================ resource "routeros_interface_ethernet_switch_crs_egress_vlan_tag" "test" { vlan_id = 100 tagged_ports = ["ether1", "ether2"] } ================================================ FILE: examples/resources/routeros_interface_ethernet_switch_crs_egress_vlan_translation/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/ethernet/switch/egress-vlan-translation get [print show-ids]] terraform import routeros_interface_ethernet_switch_crs_egress_vlan_translation.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_ethernet_switch_crs_egress_vlan_translation.test "name=xxx" ================================================ FILE: examples/resources/routeros_interface_ethernet_switch_crs_egress_vlan_translation/resource.tf ================================================ resource "routeros_interface_ethernet_switch_crs_egress_vlan_translation" "test" { ports = ["ether1"] service_vlan_format = "any" customer_vlan_format = "any" customer_vid = 100 new_customer_vid = 0 } ================================================ FILE: examples/resources/routeros_interface_ethernet_switch_crs_ingress_vlan_translation/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/ethernet/switch/ingress-vlan-translation get [print show-ids]] terraform import routeros_interface_ethernet_switch_crs_ingress_vlan_translation.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_ethernet_switch_crs_ingress_vlan_translation.test "name=xxx" ================================================ FILE: examples/resources/routeros_interface_ethernet_switch_crs_ingress_vlan_translation/resource.tf ================================================ resource "routeros_interface_ethernet_switch_crs_ingress_vlan_translation" "test" { ports = ["ether1"] service_vlan_format = "any" customer_vlan_format = "any" customer_vid = 0 new_customer_vid = 100 sa_learning = true } ================================================ FILE: examples/resources/routeros_interface_ethernet_switch_crs_vlan/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/ethernet/switch/vlan get [print show-ids]] terraform import routeros_interface_ethernet_switch_crs_vlan.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_ethernet_switch_crs_vlan.test "comment=xxx" ================================================ FILE: examples/resources/routeros_interface_ethernet_switch_crs_vlan/resource.tf ================================================ resource "routeros_interface_ethernet_switch_crs_vlan" "test" { ports = ["ether1"] vlan_id = 10 } ================================================ FILE: examples/resources/routeros_interface_ethernet_switch_host/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/ethernet/switch/host get [print show-ids]] terraform import routeros_interface_ethernet_switch_host.test *0 #Or you can import a resource using one of its attributes terraform import routeros_interface_ethernet_switch_host.test "name=xxx" ================================================ FILE: examples/resources/routeros_interface_ethernet_switch_host/resource.tf ================================================ resource "routeros_interface_ethernet_switch_host" "test" { switch = "switch1" mac_address = "00:00:00:00:00:00" ports = ["ether1"] mirror = true } ================================================ FILE: examples/resources/routeros_interface_ethernet_switch_port/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/ethernet/switch/port get [print show-ids]] terraform import routeros_interface_ethernet_switch_port.test *1 #Or you can import a resource using one of its attributes terraform import routeros_interface_ethernet_switch_port.test "name=xxx" ================================================ FILE: examples/resources/routeros_interface_ethernet_switch_port/resource.tf ================================================ resource "routeros_interface_ethernet_switch_port" "test" { name = "ether1" vlan_mode = "check" } ================================================ FILE: examples/resources/routeros_interface_ethernet_switch_port_isolation/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/ethernet/switch/port-isolation get [print show-ids]] terraform import routeros_interface_ethernet_switch_port_isolation.test *1 #Or you can import a resource using one of its attributes terraform import routeros_interface_ethernet_switch_port_isolation.test "name=xxx" ================================================ FILE: examples/resources/routeros_interface_ethernet_switch_port_isolation/resource.tf ================================================ resource "routeros_interface_ethernet_switch_port_isolation" "test" { name = "ether1" forwarding_override = "ether1" } ================================================ FILE: examples/resources/routeros_interface_ethernet_switch_rule/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/ethernet/switch/rule get [print show-ids]] terraform import routeros_interface_ethernet_switch_rule.test *0 #Or you can import a resource using one of its attributes terraform import routeros_interface_ethernet_switch_rule.test "name=xxx" ================================================ FILE: examples/resources/routeros_interface_ethernet_switch_rule/resource.tf ================================================ resource "routeros_interface_ethernet_switch_rule" "test" { switch = "switch1" ports = ["ether1"] copy_to_cpu = true } ================================================ FILE: examples/resources/routeros_interface_ethernet_switch_vlan/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/ethernet/switch/vlan get [print show-ids]] terraform import routeros_interface_ethernet_switch_vlan.test *0 #Or you can import a resource using one of its attributes terraform import routeros_interface_ethernet_switch_vlan.test "name=xxx" ================================================ FILE: examples/resources/routeros_interface_ethernet_switch_vlan/resource.tf ================================================ resource "routeros_interface_ethernet_switch_vlan" "test" { switch = "switch1" ports = ["ether1"] vlan_id = 10 independent_learning = true } ================================================ FILE: examples/resources/routeros_interface_gre/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/gre get [print show-ids]] terraform import routeros_interface_gre.gre_hq "*1" #Or you can import a resource using one of its attributes terraform import routeros_interface_gre.gre_hq "name=xxx" ================================================ FILE: examples/resources/routeros_interface_gre/resource.tf ================================================ resource "routeros_interface_gre" "gre_hq" { name = "gre-hq-1" remote_address = "10.77.3.26" disabled = true } ================================================ FILE: examples/resources/routeros_interface_gre6/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/gre6 get [print show-ids]] terraform import routeros_interface_gre6.gre_hq "*1" #Or you can import a resource using one of its attributes terraform import routeros_interface_gre6.gre_hq "name=gre-hq-ipv6" ================================================ FILE: examples/resources/routeros_interface_gre6/resource.tf ================================================ resource "routeros_interface_gre6" "gre_hq" { name = "gre-hq-ipv6" remote_address = "2a02::2" disabled = true } ================================================ FILE: examples/resources/routeros_interface_ipip/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/ipip get [print show-ids]] terraform import routeros_interface_ipip.ipip_hq "*1" #Or you can import a resource using one of its attributes terraform import routeros_interface_ipip.ipip_hq "name=xxx" ================================================ FILE: examples/resources/routeros_interface_ipip/resource.tf ================================================ resource "routeros_interface_ipip" "ipip_hq" { name = "ipip-hq-1" remote_address = "10.77.3.26" disabled = true } ================================================ FILE: examples/resources/routeros_interface_l2tp_client/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/l2tp/client get [print show-ids]] terraform import routeros_interface_l2tp_client.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_l2tp_client.test "name=xxx" ================================================ FILE: examples/resources/routeros_interface_l2tp_client/resource.tf ================================================ resource "routeros_interface_l2tp_client" "test" { name = "l2tp-test-out" connect_to = "127.0.0.1" user = "aaa" password = "bbb" } ================================================ FILE: examples/resources/routeros_interface_l2tp_server/import.sh ================================================ terraform import routeros_interface_l2tp_server.test . ================================================ FILE: examples/resources/routeros_interface_l2tp_server/resource.tf ================================================ resource "routeros_interface_l2tp_server" "test" { enabled = true } ================================================ FILE: examples/resources/routeros_interface_list/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/list get [print show-ids]] terraform import routeros_interface_list.list "*2000010" #Or you can import a resource using one of its attributes terraform import routeros_interface_list.list "name=xxx" ================================================ FILE: examples/resources/routeros_interface_list/resource.tf ================================================ resource "routeros_interface_list" "list" { name = "my-list" } ================================================ FILE: examples/resources/routeros_interface_list_member/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/list/member get [print show-ids]] terraform import routeros_interface_list_member.list_member "*0" #Or you can import a resource using one of its attributes terraform import routeros_interface_list_member.list_member "name=xxx" ================================================ FILE: examples/resources/routeros_interface_list_member/resource.tf ================================================ resource "routeros_interface_list_member" "list_member" { interface = "ether1" list = "my-list" } ================================================ FILE: examples/resources/routeros_interface_lte/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ get [print show-ids]] terraform import routeros_interface_lte.test *3 ================================================ FILE: examples/resources/routeros_interface_lte/resource.tf ================================================ resource "routeros_interface_lte" "test" { allow_roaming = false apn_profiles = "default" band = [] default_name = "lte1" disabled = false mtu = "1500" name = "lte1" network_mode = ["3g", "lte"] sms_protocol = null } ================================================ FILE: examples/resources/routeros_interface_lte_apn/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ get [print show-ids]] terraform import routeros_interface_lte_apn.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_lte_apn.test "name=xxx" ================================================ FILE: examples/resources/routeros_interface_lte_apn/resource.tf ================================================ resource "routeros_interface_lte_apn" "test" { name = "apn1" apn = "internet" authentication = "pap" } ================================================ FILE: examples/resources/routeros_interface_macvlan/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/macvlan get [print show-ids]] terraform import routeros_interface_macvlan.test "*0" #Or you can import a resource using one of its attributes terraform import routeros_interface_macvlan.test "name=xxx" ================================================ FILE: examples/resources/routeros_interface_macvlan/resource.tf ================================================ resource "routeros_interface_macvlan" "test" { interface = "ether1" name = "macvlan1" disabled = false } ================================================ FILE: examples/resources/routeros_interface_ovpn_server/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/ovpn-server get [print show-ids]] terraform import routeros_interface_ovpn_server.user1 *29 #Or you can import a resource using one of its attributes terraform import routeros_interface_ovpn_server.user1 "name=xxx" ================================================ FILE: examples/resources/routeros_interface_ovpn_server/resource.tf ================================================ resource "routeros_interface_ovpn_server" "user1" { name = "ovpn-in1" user = "user1" depends_on = [routeros_ovpn_server.server] } ================================================ FILE: examples/resources/routeros_interface_pppoe_client/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/pppoe-client get [print show-ids]] terraform import routeros_interface_pppoe_client.test "*0" #Or you can import a resource using one of its attributes terraform import routeros_interface_pppoe_client.test "name=xxx" ================================================ FILE: examples/resources/routeros_interface_pppoe_client/resource.tf ================================================ resource "routeros_interface_pppoe_client" "test" { interface = "ether1" password = "StrongPass" service_name = "pppoeservice" name = "PPPoE-Out" disabled = false user = "MT-User" } ================================================ FILE: examples/resources/routeros_interface_pppoe_server/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/pppoe/server get [print show-ids]] terraform import routeros_interface_pppoe_server.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_pppoe_server.test "name=xxx" ================================================ FILE: examples/resources/routeros_interface_pppoe_server/resource.tf ================================================ resource "routeros_interface_pppoe_server" "test" { comment = "comment" disabled = true name = "pppoe-in1" user = "" service = "" } ================================================ FILE: examples/resources/routeros_interface_veth/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/veth get [print show-ids]] terraform import routeros_interface_veth.test "*0" #Or you can import a resource using one of its attributes terraform import routeros_interface_veth.test "name=xxx" ================================================ FILE: examples/resources/routeros_interface_veth/resource.tf ================================================ resource "routeros_interface_veth" "test" { name = "veth-test" address = ["192.168.120.2/24"] gateway = "192.168.120.1" comment = "Virtual interface" } ================================================ FILE: examples/resources/routeros_interface_vlan/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/vlan get [print show-ids]] terraform import routeros_interface_vlan.interface_vlan "*1" #Or you can import a resource using one of its attributes terraform import routeros_interface_vlan.interface_vlan "name=xxx" ================================================ FILE: examples/resources/routeros_interface_vlan/resource.tf ================================================ resource "routeros_interface_vlan" "interface_vlan" { interface = "bridge" name = "VLAN_TEST" vlan_id = 50 } ================================================ FILE: examples/resources/routeros_interface_vrrp/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/vrrp get [print show-ids]] terraform import routeros_interface_vrrp.interface_vrrp "*0" #Or you can import a resource using one of its attributes terraform import routeros_interface_vrrp.interface_vrrp "name=xxx" ================================================ FILE: examples/resources/routeros_interface_vrrp/resource.tf ================================================ resource "routeros_interface_vrrp" "interface_vrrp" { interface = "bridge" name = "lan_vrrp" } ================================================ FILE: examples/resources/routeros_interface_vxlan/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/vxlan get [print show-ids]] terraform import routeros_interface_vxlan.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_vxlan.test "name=vxlan1" ================================================ FILE: examples/resources/routeros_interface_vxlan/resource.tf ================================================ resource "routeros_interface_vxlan" "test" { name = "vxlan1-test" vni = 10 } ================================================ FILE: examples/resources/routeros_interface_vxlan_vteps/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/vxlan/vteps get [print show-ids]] terraform import routeros_interface_vxlan_vteps.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_vxlan_vteps.test "interface=vxlan2-test" ================================================ FILE: examples/resources/routeros_interface_vxlan_vteps/resource.tf ================================================ resource "routeros_interface_vxlan" "test-2" { name = "vxlan2-test" vni = 11 } resource "routeros_interface_vxlan_vteps" "test" { interface = routeros_interface_vxlan.test-2.name remote_ip = "192.168.10.10" } ================================================ FILE: examples/resources/routeros_interface_w60g/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/w60g get [print show-ids]] terraform import routeros_interface_w60g.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_w60g.test "name=xxx" ================================================ FILE: examples/resources/routeros_interface_w60g/resource.tf ================================================ resource "routeros_interface_w60g" "test" { make = "wlan60-1" password = "put_your_safe_password_here" ssid = "put_your_new_ssid_here" disabled = false mode = "ap-bridge" } ================================================ FILE: examples/resources/routeros_interface_w60g_station/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/w60g/station get [print show-ids]] terraform import routeros_interface_w60g_station.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_w60g_station.test "name=xxx" ================================================ FILE: examples/resources/routeros_interface_w60g_station/resource.tf ================================================ resource "routeros_interface_w60g_station" "test" { name = "wlan60-station-1" parent = "wlan60-1" remote-address = "AA:AA:AA:AA:AA:AA" put-in-bridge = "parent" } ================================================ FILE: examples/resources/routeros_interface_wireguard/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wireguard get [print show-ids]] terraform import routeros_interface_wireguard.test_wg_interface "*1" #Or you can import a resource using one of its attributes terraform import routeros_interface_wireguard.test_wg_interface "name=xxx" ================================================ FILE: examples/resources/routeros_interface_wireguard/resource.tf ================================================ resource "routeros_interface_wireguard" "test_wg_interface" { name = "test_wg_interface" listen_port = "13231" } ================================================ FILE: examples/resources/routeros_interface_wireguard_peer/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wireguard/peers get [print show-ids]] terraform import routeros_interface_wireguard_peer.wg_peer "*0" #Or you can import a resource using one of its attributes terraform import routeros_interface_wireguard_peer.wg_peer "name=xxx" ================================================ FILE: examples/resources/routeros_interface_wireguard_peer/resource.tf ================================================ resource "routeros_interface_wireguard" "test_wg_interface" { name = "test_wg_interface" listen_port = "13231" } resource "routeros_interface_wireguard_peer" "wg_peer" { interface = routeros_interface_wireguard.test_wg_interface.name public_key = "MY_BASE_64_PUBLIC_KEY" allowed_address = [ "192.168.0.0/16", "172.16.0.0/12", "10.0.0.0/8", ] } ================================================ FILE: examples/resources/routeros_interface_wireless/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wireless get [print show-ids]] terraform import routeros_interface_wireless.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_wireless.test "name=xxx" ================================================ FILE: examples/resources/routeros_interface_wireless/resource.tf ================================================ variable "wlan_2ghz_disabled" { type = bool default = false } resource "routeros_interface_wireless" "wlan-2ghz" { name = "wlan1" disabled = var.wlan_2ghz_disabled } resource "routeros_interface_wireless_security_profiles" "test" { name = "test-profile" mode = "dynamic-keys" authentication_types = ["wpa-psk", "wpa2-psk"] wpa_pre_shared_key = "wpa_psk_key" wpa2_pre_shared_key = "wpa2_psk_key" } resource "routeros_interface_wireless" "test" { depends_on = [resource.routeros_interface_wireless_security_profiles.test] security_profile = resource.routeros_interface_wireless_security_profiles.test.name mode = "ap-bridge" master_interface = resource.routeros_interface_wireless.wlan-2ghz.name name = "wlan-guest" ssid = "guests" basic_rates_ag = ["6Mbps", "9Mbps"] } ================================================ FILE: examples/resources/routeros_interface_wireless_access_list/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wireless/access-list get [print show-ids]] terraform import routeros_interface_wireless_access_list.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_wireless_access_list.test "name=xxx" ================================================ FILE: examples/resources/routeros_interface_wireless_access_list/resource.tf ================================================ resource "routeros_interface_wireless_access_list" "test" { signal_range = "-100..100" time = "3h3m-5h,mon,tue,wed,thu,fri" mac_address = "00:AA:BB:CC:DD:EE" } ================================================ FILE: examples/resources/routeros_interface_wireless_cap/import.sh ================================================ terraform import routeros_interface_wireless_cap.settings . #Or you can import a resource using one of its attributes terraform import routeros_interface_wireless_cap.settings "name=xxx" ================================================ FILE: examples/resources/routeros_interface_wireless_cap/resource.tf ================================================ resource "routeros_interface_wireless_cap" "settings" { discovery_interfaces = ["bridge1"] enabled = true interfaces = ["wlan1", "wlan2"] } ================================================ FILE: examples/resources/routeros_interface_wireless_connect_list/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wireless/connect-list get [print show-ids]] terraform import routeros_interface_wireless_connect_list.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_wireless_connect_list.test "name=xxx" ================================================ FILE: examples/resources/routeros_interface_wireless_connect_list/resource.tf ================================================ resource "routeros_interface_wireless_connect_list" "test" { interface = "wlan0" security_profile = "test-secp" } ================================================ FILE: examples/resources/routeros_interface_wireless_security_profiles/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wireless/security-profiles get [print show-ids]] terraform import routeros_interface_wireless_security_profiles.test *3 #Or you can import a resource using one of its attributes terraform import routeros_interface_wireless_security_profiles.test "name=xxx" ================================================ FILE: examples/resources/routeros_interface_wireless_security_profiles/resource.tf ================================================ resource "routeros_interface_wireless_security_profiles" "test" { name = "test-profile" mode = "dynamic-keys" authentication_types = ["wpa-psk", "wpa2-psk"] wpa_pre_shared_key = "wpa_psk_key" wpa2_pre_shared_key = "wpa2_psk_key" } ================================================ FILE: examples/resources/routeros_ip_address/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/address get [print show-ids]] terraform import routeros_ip_address.address "*0" #Or you can import a resource using one of its attributes terraform import routeros_ip_address.address "name=xxx" ================================================ FILE: examples/resources/routeros_ip_address/resource.tf ================================================ resource "routeros_ip_address" "address" { address = "10.0.0.1/24" interface = "bridge" network = "10.0.0.0" } ================================================ FILE: examples/resources/routeros_ip_cloud/import.sh ================================================ terraform import routeros_ip_cloud.test . ================================================ FILE: examples/resources/routeros_ip_cloud/resource.tf ================================================ resource "routeros_ip_cloud" "test" { ddns_enabled = true update_time = false ddns_update_interval = "11m" } ================================================ FILE: examples/resources/routeros_ip_cloud_advanced/import.sh ================================================ terraform import routeros_ip_cloud_advanced.settings . ================================================ FILE: examples/resources/routeros_ip_cloud_advanced/resource.tf ================================================ resource "routeros_ip_cloud_advanced" "settings" { use_local_address = true } ================================================ FILE: examples/resources/routeros_ip_dhcp_client/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/dhcp-client get [print show-ids]] terraform import routeros_ip_dhcp_client.client "*0" #Or you can import a resource using one of its attributes terraform import routeros_ip_dhcp_client.client "name=xxx" ================================================ FILE: examples/resources/routeros_ip_dhcp_client/resource.tf ================================================ resource "routeros_ip_dhcp_client" "client" { interface = "bridge" } ================================================ FILE: examples/resources/routeros_ip_dhcp_client_option/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/dhcp-client/option get [print show-ids]] terraform import routeros_ip_dhcp_client_option.option "*0" #Or you can import a resource using one of its attributes terraform import routeros_ip_dhcp_client_option.option "name=xxx" ================================================ FILE: examples/resources/routeros_ip_dhcp_client_option/resource.tf ================================================ resource "routeros_ip_dhcp_client_option" "option" { name = "my-dhcp-option" code = 60 } ================================================ FILE: examples/resources/routeros_ip_dhcp_relay/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/dhcp-relay get [print show-ids]] terraform import routeros_ip_dhcp_relay.relay "*0" #Or you can import a resource using one of its attributes terraform import routeros_ip_dhcp_relay.relay "name=xxx" ================================================ FILE: examples/resources/routeros_ip_dhcp_relay/resource.tf ================================================ resource "routeros_ip_dhcp_relay" "relay" { name = "test relay" interface = "ether1" dhcp_server = "0.0.0.1" } ================================================ FILE: examples/resources/routeros_ip_dhcp_server/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/dhcp-server get [print show-ids]] terraform import routeros_ip_dhcp_server.server "*1" ================================================ FILE: examples/resources/routeros_ip_dhcp_server/resource.tf ================================================ resource "routeros_ip_dhcp_server" "server" { address_pool = "my_address_pool" interface = "bridge" name = "bridge_dhcp" } ================================================ FILE: examples/resources/routeros_ip_dhcp_server_config/import.sh ================================================ terraform import routeros_ip_dhcp_server_config.settings . ================================================ FILE: examples/resources/routeros_ip_dhcp_server_config/resource.tf ================================================ resource "routeros_ip_dhcp_server_config" "settings" { accounting = true interim_update = "1m" radius_password = "same-as-user" store_leases_disk = "10m" } ================================================ FILE: examples/resources/routeros_ip_dhcp_server_lease/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/dhcp-server/lease get [print show-ids]] terraform import routeros_ip_dhcp_server_lease.dhcp_lease "*0" #Or you can import a resource using one of its attributes terraform import routeros_ip_dhcp_server_lease.dhcp_lease "name=xxx" ================================================ FILE: examples/resources/routeros_ip_dhcp_server_lease/resource.tf ================================================ resource "routeros_ip_dhcp_server_lease" "dhcp_lease" { address = "10.0.0.2" mac_address = "AA:BB:CC:DD:11:22" } ================================================ FILE: examples/resources/routeros_ip_dhcp_server_network/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/dhcp-server/network get [print show-ids]] terraform import routeros_ip_dhcp_server_network.dhcp_server_network "*0" #Or you can import a resource using one of its attributes terraform import routeros_ip_dhcp_server_network.dhcp_server_network "name=xxx" ================================================ FILE: examples/resources/routeros_ip_dhcp_server_network/resource.tf ================================================ resource "routeros_ip_dhcp_server_network" "dhcp_server_network" { address = "10.0.0.0/24" gateway = "10.0.0.1" dns_server = ["1.1.1.1"] } ================================================ FILE: examples/resources/routeros_ip_dhcp_server_option/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/dhcp-server/option/get [print show-ids]] terraform import routeros_ip_dhcp_server_option.tftp_option "*1" #Or you can import a resource using one of its attributes terraform import routeros_ip_dhcp_server_option.tftp_option "name=xxx" ================================================ FILE: examples/resources/routeros_ip_dhcp_server_option/resource.tf ================================================ resource "routeros_ip_dhcp_server_option" "jumbo_frame_opt" { code = 77 name = "jumbo-mtu-opt" value = "0x2336" } resource "routeros_ip_dhcp_server_option" "tftp_option" { code = 66 name = "tftpserver-66" value = "s'10.10.10.22'" } ================================================ FILE: examples/resources/routeros_ip_dhcp_server_option_matcher/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/dhcp-server/matcher/get [print show-ids]] terraform import routeros_ip_dhcp_server_option_matcher.test "*1" ================================================ FILE: examples/resources/routeros_ip_dhcp_server_option_matcher/resource.tf ================================================ resource "routeros_ip_dhcp_server_option_matcher" "dhcp1_ip_by_vendor_class" { name = "dhcp1_ip_by_vendor_class" server = "dhcp1" address_pool = "pool1" code = 60 # Vendor Class Identifier value = "android-dhcp-11" matching_type = "exact" } ================================================ FILE: examples/resources/routeros_ip_dhcp_server_option_sets/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/dhcp-server/option/sets/get [print show-ids]] terraform import routeros_ip_dhcp_server_option_sets.lan_option_set "*1" ================================================ FILE: examples/resources/routeros_ip_dhcp_server_option_sets/resource.tf ================================================ resource "routeros_ip_dhcp_server_option" "jumbo_frame_opt" { code = 77 name = "jumbo-mtu-opt" value = "0x2336" } resource "routeros_ip_dhcp_server_option" "tftp_option" { code = 66 name = "tftpserver-66" value = "s'10.10.10.22'" } resource "routeros_ip_dhcp_server_option_sets" "lan_option_set" { name = "lan-option-set" options = join(",", [routeros_ip_dhcp_server_option.jumbo_frame_opt.name, routeros_ip_dhcp_server_option.tftp_option.name]) } ================================================ FILE: examples/resources/routeros_ip_dns/import.sh ================================================ #The DNS Settings can not be imported. #Terraform will ignore the current settings and will overwrite the current settings with the settings defined in Terraform. ================================================ FILE: examples/resources/routeros_ip_dns/resource.tf ================================================ resource "routeros_ip_dns" "dns-server" { allow_remote_requests = true servers = [ "2606:4700:4700::1111,1.1.1.1", "2606:4700:4700::1001,1.0.0.1", ] } ================================================ FILE: examples/resources/routeros_ip_dns_adlist/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/dns/adlist get [print show-ids]] terraform import routeros_ip_dns_adlist.test "*0" #Or you can import a resource using one of its attributes terraform import routeros_ip_dns_adlist.test "name=xxx" ================================================ FILE: examples/resources/routeros_ip_dns_adlist/resource.tf ================================================ resource "routeros_ip_dns_adlist" "test" { url = "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts" ssl_verify = false } ================================================ FILE: examples/resources/routeros_ip_dns_forwarders/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/dns/forwarders get [print show-ids]] terraform import routeros_ip_dns_forwarders.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_dns_forwarders.test "name=xxx" ================================================ FILE: examples/resources/routeros_ip_dns_forwarders/resource.tf ================================================ resource "routeros_ip_dns_forwarders" "test" { disabled = true dns_servers = ["1.1.1.1"] doh_servers = ["2.2.2.2"] name = "test" verify_doh_cert = "false" } ================================================ FILE: examples/resources/routeros_ip_dns_record/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/dns/static get [print show-ids]] terraform import routeros_ip_dns_record.name_record "*0" #Or you can import a resource using one of its attributes terraform import routeros_ip_dns_record.name_record "name=xxx" ================================================ FILE: examples/resources/routeros_ip_dns_record/resource.tf ================================================ resource "routeros_ip_dns_record" "name_record" { name = "router.lan" address = "192.168.88.1" type = "A" } resource "routeros_ip_dns_record" "regexp_record" { regexp = ".*pool.ntp.org" address = "192.168.88.1" type = "A" } resource "routeros_dns_record" "aaaa_record" { name = "ipv6.lan" address = "ff00::1" type = "AAAA" } resource "routeros_dns_record" "cname_record" { name = "cname.lan" cname = "ipv4.lan" type = "CNAME" } resource "routeros_dns_record" "fwd_record" { name = "fwd.lan" forward_to = "127.0.0.1" type = "FWD" } resource "routeros_dns_record" "mx_record" { name = "mx.lan" mx_exchange = "127.0.0.1" mx_preference = 10 type = "MX" } resource "routeros_dns_record" "ns_record" { name = "ns.lan" ns = "127.0.0.1" type = "NS" } resource "routeros_dns_record" "nxdomain_record" { name = "nxdomain.lan" type = "NXDOMAIN" } resource "routeros_dns_record" "srv_record" { name = "srv.lan" srv_port = 8080 srv_priority = 10 srv_target = "127.0.0.1" srv_weight = 100 type = "SRV" } resource "routeros_dns_record" "txt_record" { name = "_acme-challenge.yourwebsite.com" text = "dW6MrI3nBy3eJgYWH3QAg1Cwk_TvjFESOuKo+mp6nm1" type = "TXT" } ================================================ FILE: examples/resources/routeros_ip_firewall_addr_list/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/firewall/address-list get [print show-ids]] terraform import routeros_ip_firewall_addr_list.example_list "*0" #Or you can import a resource using one of its attributes terraform import routeros_ip_firewall_addr_list.example_list "name=xxx" ================================================ FILE: examples/resources/routeros_ip_firewall_addr_list/resource.tf ================================================ resource "routeros_ip_firewall_addr_list" "example_list" { address = "1.1.1.1" list = "Example List" } ================================================ FILE: examples/resources/routeros_ip_firewall_connection_tracking/resource.tf ================================================ resource "routeros_ip_firewall_connection_tracking" "data" { enabled = "yes" generic_timeout = "3m" icmp_timeout = "3m" loose_tcp_tracking = "false" tcp_close_timeout = "3m" tcp_close_wait_timeout = "3m" tcp_established_timeout = "3m" tcp_fin_wait_timeout = "3m" tcp_last_ack_timeout = "3m" tcp_max_retrans_timeout = "3m" tcp_syn_received_timeout = "3m" tcp_syn_sent_timeout = "3m" tcp_time_wait_timeout = "3m" tcp_unacked_timeout = "3m" udp_stream_timeout = "3m" udp_timeout = "3m" } ================================================ FILE: examples/resources/routeros_ip_firewall_filter/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/firewall/filter get [print show-ids]] terraform import routeros_ip_firewall_filter.rule "*0" #Or you can import a resource using one of its attributes terraform import routeros_ip_firewall_filter.rule "name=xxx" ================================================ FILE: examples/resources/routeros_ip_firewall_filter/resource.tf ================================================ resource "routeros_ip_firewall_filter" "rule" { action = "accept" chain = "forward" src_address = "10.0.0.1" dst_address = "10.0.1.1" dst_port = "443" protocol = "tcp" } ================================================ FILE: examples/resources/routeros_ip_firewall_layer7_protocol/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/firewall/layer7-protocol get [print show-ids]] terraform import routeros_ip_firewall_layer7_protocol.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_firewall_layer7_protocol.test "name=xxx" ================================================ FILE: examples/resources/routeros_ip_firewall_layer7_protocol/resource.tf ================================================ resource "routeros_ip_firewall_layer7_protocol" "test" { name = "rdp" regexp = "rdpdr.*cliprdr.*rdpsnd" } ================================================ FILE: examples/resources/routeros_ip_firewall_mangle/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/firewall/mangle get [print show-ids]] terraform import routeros_ip_firewall_mangle.rule "*0" #Or you can import a resource using one of its attributes terraform import routeros_ip_firewall_mangle.rule "name=xxx" ================================================ FILE: examples/resources/routeros_ip_firewall_mangle/resource.tf ================================================ resource "routeros_ip_firewall_mangle" "rule" { action = "change-mss" chain = "forward" out_interface = "pppoe-out" protocol = "tcp" tcp_flags = "syn" new_mss = "1130" tcp_mss = "1301-65535" } ================================================ FILE: examples/resources/routeros_ip_firewall_nat/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/firewall/nat get [print show-ids]] terraform import routeros_ip_firewall_nat.rule "*0" #Or you can import a resource using one of its attributes terraform import routeros_ip_firewall_nat.rule "name=xxx" ================================================ FILE: examples/resources/routeros_ip_firewall_nat/resource.tf ================================================ resource "routeros_ip_firewall_nat" "rule" { action = "masquerade" chain = "srcnat" out_interface = "ether16" } ================================================ FILE: examples/resources/routeros_ip_hotspot/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/hotspot get [print show-ids]] terraform import routeros_ip_hotspot.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_hotspot.test "name=xxx" ================================================ FILE: examples/resources/routeros_ip_hotspot/resource.tf ================================================ resource "routeros_ip_hotspot" "test" { name = "server-1" interface = "ether2" } ================================================ FILE: examples/resources/routeros_ip_hotspot_ip_binding/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/hotspot/ip-binding get [print show-ids]] terraform import routeros_ip_hotspot_ip_binding.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_hotspot_ip_binding.test "name=xxx" ================================================ FILE: examples/resources/routeros_ip_hotspot_ip_binding/resource.tf ================================================ resource "routeros_ip_hotspot_ip_binding" "test" { address = "0.0.0.1" comment = "comment" mac_address = "00:00:00:00:01:10" to_address = "0.0.0.2" } ================================================ FILE: examples/resources/routeros_ip_hotspot_profile/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/hotspot/profile get [print show-ids]] terraform import routeros_ip_hotspot_profile.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_hotspot_profile.test "name=xxx" ================================================ FILE: examples/resources/routeros_ip_hotspot_profile/resource.tf ================================================ resource "routeros_ip_hotspot_profile" "test" { name = "hsprof-1" login_by = ["mac", "https", "trial"] use_radius = true } ================================================ FILE: examples/resources/routeros_ip_hotspot_service_port/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/hotspot/service-port get [print show-ids]] terraform import routeros_ip_hotspot_service_port.test *1 #Or you can import a resource using one of its attributes terraform import routeros_ip_hotspot_service_port.test "name=xxx" ================================================ FILE: examples/resources/routeros_ip_hotspot_service_port/resource.tf ================================================ resource "routeros_ip_hotspot_service_port" "test" { name = "ftp" disabled = true } ================================================ FILE: examples/resources/routeros_ip_hotspot_user/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/hotspot/user get [print show-ids]] terraform import routeros_ip_hotspot_user.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_hotspot_user.test "name=xxx" ================================================ FILE: examples/resources/routeros_ip_hotspot_user/resource.tf ================================================ resource "routeros_ip_hotspot_user" "test" { name = "user-1" } ================================================ FILE: examples/resources/routeros_ip_hotspot_user_profile/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/hotspot/user/profile get [print show-ids]] terraform import routeros_ip_hotspot_user_profile.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_hotspot_user_profile.test "name=xxx" ================================================ FILE: examples/resources/routeros_ip_hotspot_user_profile/resource.tf ================================================ resource "routeros_ip_hotspot_user_profile" "test" { add_mac_cookie = true address_list = "list-1" idle_timeout = "none" keepalive_timeout = "2m" mac_cookie_timeout = "3d" name = "new-profile" shared_users = 3 status_autorefresh = "2m" transparent_proxy = true advertise = true } ================================================ FILE: examples/resources/routeros_ip_hotspot_walled_garden/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/hotspot/walled-garden get [print show-ids]] terraform import routeros_ip_hotspot_walled_garden.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_hotspot_walled_garden.test "name=xxx" ================================================ FILE: examples/resources/routeros_ip_hotspot_walled_garden/resource.tf ================================================ resource "routeros_ip_hotspot_walled_garden" "test" { action = "deny" dst_host = "1.2.3.4" dst_port = "!443" } ================================================ FILE: examples/resources/routeros_ip_hotspot_walled_garden_ip/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/hotspot/walled-garden/ip get [print show-ids]] terraform import routeros_ip_hotspot_walled_garden_ip.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_hotspot_walled_garden_ip.test "name=xxx" ================================================ FILE: examples/resources/routeros_ip_hotspot_walled_garden_ip/resource.tf ================================================ resource "routeros_ip_hotspot_walled_garden_ip" "test" { action = "reject" dst_address = "!0.0.0.0" dst_address_list = "dlist" dst_port = "0-65535" protocol = "tcp" src_address = "0.0.0.0" src_address_list = "slist" } ================================================ FILE: examples/resources/routeros_ip_ipsec_identity/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/ipsec/identity get [print show-ids]] terraform import routeros_ip_ipsec_identity.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_ipsec_identity.test "peer=NordVPN" ================================================ FILE: examples/resources/routeros_ip_ipsec_identity/resource.tf ================================================ resource "routeros_ip_ipsec_mode_config" "test" { name = "NordVPN" responder = false } resource "routeros_ip_ipsec_peer" "test" { address = "lv20.nordvpn.com" exchange_mode = "ike2" name = "NordVPN" } resource "routeros_ip_ipsec_identity" "test" { auth_method = "eap" certificate = "" eap_methods = "eap-mschapv2" generate_policy = "port-strict" mode_config = routeros_ip_ipsec_mode_config.test.name peer = routeros_ip_ipsec_peer.test.name username = "support@mikrotik.com" password = "secret" } ================================================ FILE: examples/resources/routeros_ip_ipsec_key/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/ipsec/key get [print show-ids]] terraform import routeros_ip_ipsec_key.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_ipsec_key.test "name=test-key" ================================================ FILE: examples/resources/routeros_ip_ipsec_key/resource.tf ================================================ resource "routeros_ip_ipsec_key" "test" { name = "test-key" key_size = 2048 } ================================================ FILE: examples/resources/routeros_ip_ipsec_mode_config/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/ipsec/mode/config get [print show-ids]] terraform import routeros_ip_ipsec_mode_config.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_ipsec_mode_config.test "address=1.2.3.4" ================================================ FILE: examples/resources/routeros_ip_ipsec_mode_config/resource.tf ================================================ resource "routeros_ip_ipsec_mode_config" "test" { name = "test-cfg" address = "1.2.3.4" split_include = ["0.0.0.0/0"] split_dns = ["1.1.1.1"] } ================================================ FILE: examples/resources/routeros_ip_ipsec_peer/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/ipsec/peer get [print show-ids]] terraform import routeros_ip_ipsec_peer.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_ipsec_peer.test "name=NordVPN" ================================================ FILE: examples/resources/routeros_ip_ipsec_peer/resource.tf ================================================ resource "routeros_ip_ipsec_peer" "test" { address = "lv20.nordvpn.com" exchange_mode = "ike2" name = "NordVPN" } ================================================ FILE: examples/resources/routeros_ip_ipsec_policy/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/ipsec/policy get [print show-ids]] terraform import routeros_ip_ipsec_policy.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_ipsec_policy.test "group=test-group" ================================================ FILE: examples/resources/routeros_ip_ipsec_policy/resource.tf ================================================ resource "routeros_ip_ipsec_policy_group" "group-for-policy" { name = "test-group" } resource "routeros_ip_ipsec_policy" "policy" { dst_address = "0.0.0.0/0" group = routeros_ip_ipsec_policy_group.group-for-policy.name proposal = "NordVPN" src_address = "0.0.0.0/0" template = true } ================================================ FILE: examples/resources/routeros_ip_ipsec_policy_group/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/ipsec/policy/group get [print show-ids]] terraform import routeros_ip_ipsec_policy_group.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_ipsec_policy_group.test "name=test-group" ================================================ FILE: examples/resources/routeros_ip_ipsec_policy_group/resource.tf ================================================ resource "routeros_ip_ipsec_policy_group" "test" { name = "test-group" } ================================================ FILE: examples/resources/routeros_ip_ipsec_profile/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/ipsec/profile get [print show-ids]] terraform import routeros_ip_ipsec_profile.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_ipsec_profile.test "name=test-profile" ================================================ FILE: examples/resources/routeros_ip_ipsec_profile/resource.tf ================================================ resource "routeros_ip_ipsec_profile" "test" { name = "test-profile" hash_algorithm = "sha256" enc_algorithm = ["aes-192", "aes-256"] dh_group = ["ecp384", "ecp521"] nat_traversal = false } ================================================ FILE: examples/resources/routeros_ip_ipsec_proposal/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/ipsec/proposal get [print show-ids]] terraform import routeros_ip_ipsec_proposal.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_ipsec_proposal.test "name=NordVPN" ================================================ FILE: examples/resources/routeros_ip_ipsec_proposal/resource.tf ================================================ resource "routeros_ip_ipsec_proposal" "test" { name = "NordVPN" pfs_group = "none" lifetime = "45m10s" } ================================================ FILE: examples/resources/routeros_ip_ipsec_settings/import.sh ================================================ terraform import routeros_ip_ipsec_settings.test . ================================================ FILE: examples/resources/routeros_ip_ipsec_settings/resource.tf ================================================ resource "routeros_ip_ipsec_settings" "test" { xauth_use_radius = true interim_update = "60s" } ================================================ FILE: examples/resources/routeros_ip_nat_pmp/import.sh ================================================ terraform import routeros_ip_nat_pmp.test . ================================================ FILE: examples/resources/routeros_ip_nat_pmp/resource.tf ================================================ resource "routeros_ip_nat_pmp" "test" { enabled = true } ================================================ FILE: examples/resources/routeros_ip_nat_pmp_interfaces/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/nat-pmp/interfaces get [print show-ids]] terraform import routeros_ip_nat_pmp_interfaces.test '*1' #Or you can import a resource using one of its attributes terraform import routeros_ip_nat_pmp_interfaces.test "name=xxx" ================================================ FILE: examples/resources/routeros_ip_nat_pmp_interfaces/resource.tf ================================================ resource "routeros_ip_nat_pmp_interfaces" "test" { interface = "ether1" type = "external" forced_ip = "0.0.0.0" } ================================================ FILE: examples/resources/routeros_ip_neighbor_discovery_settings/import.sh ================================================ terraform import routeros_ip_neighbor_discovery_settings.test . ================================================ FILE: examples/resources/routeros_ip_neighbor_discovery_settings/resource.tf ================================================ resource "routeros_ip_neighbor_discovery_settings" "test" { discover_interface_list = "static" lldp_med_net_policy_vlan = "1" mode = "tx-and-rx" protocol = [] } ================================================ FILE: examples/resources/routeros_ip_pool/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/pool get [print show-ids]] terraform import routeros_ip_pool.pool "*1" ================================================ FILE: examples/resources/routeros_ip_pool/resource.tf ================================================ resource "routeros_ip_pool" "pool" { name = "my_ip_pool" ranges = ["10.0.0.100-10.0.0.200"] } ================================================ FILE: examples/resources/routeros_ip_route/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/route get [print show-ids]] terraform import routeros_ip_route.a_route "*0" #Or you can import a resource using one of its attributes terraform import routeros_ip_route.a_route "name=xxx" ================================================ FILE: examples/resources/routeros_ip_route/resource.tf ================================================ resource "routeros_ip_route" "a_route" { dst_address = "0.0.0.0/0" gateway = "10.0.0.1" } ================================================ FILE: examples/resources/routeros_ip_service/import.sh ================================================ # Import with the name of the ip service in case of the example use www-ssl terraform import routeros_ip_service.www_ssl www-ssl #Or you can import a resource using one of its attributes terraform import routeros_ip_service.www_ssl "name=xxx" ================================================ FILE: examples/resources/routeros_ip_service/resource.tf ================================================ locals { tls_service = { "api-ssl" = 8729, "www-ssl" = 443 } disable_service = { "api" = 8728, "ftp" = 21, "telnet" = 23, "www" = 80 } enable_service = { "ssh" = 22, "winbox" = 8291 } } resource "routeros_system_certificate" "tls_cert" { name = "tls-cert" common_name = "Mikrotik Router" days_valid = 3650 key_usage = ["key-cert-sign", "crl-sign", "digital-signature", "key-agreement", "tls-server"] key_size = "prime256v1" sign { } } # terraform state rm 'routeros_ip_service.tls["www-ssl"]' # terraform import 'routeros_ip_service.tls["www-ssl"]' www-ssl resource "routeros_ip_service" "tls" { for_each = local.tls_service numbers = each.key port = each.value certificate = routeros_system_certificate.tls_cert.name tls_version = "only-1.2" disabled = false } resource "routeros_ip_service" "disabled" { for_each = local.disable_service numbers = each.key port = each.value disabled = true } resource "routeros_ip_service" "enabled" { for_each = local.enable_service numbers = each.key port = each.value disabled = false } ================================================ FILE: examples/resources/routeros_ip_settings/import.sh ================================================ terraform import routeros_ip_settings.settings . ================================================ FILE: examples/resources/routeros_ip_settings/resource.tf ================================================ resource "routeros_ip_settings" "settings" { ipv4_multipath_hash_policy = "l3-inner" } ================================================ FILE: examples/resources/routeros_ip_smb/import.sh ================================================ terraform import routeros_ip_smb.test . ================================================ FILE: examples/resources/routeros_ip_smb/resource.tf ================================================ resource "routeros_ip_smb" "test" { enabled = "auto" domain = "MSHOME" comment = "MikrotikSMB" interfaces = ["all"] } ================================================ FILE: examples/resources/routeros_ip_ssh_server/import.sh ================================================ terraform import routeros_ip_ssh_server.test . ================================================ FILE: examples/resources/routeros_ip_ssh_server/resource.tf ================================================ resource "routeros_ip_ssh_server" "test" { strong_crypto = true forwarding_enabled = "local" host_key_size = 4096 } ================================================ FILE: examples/resources/routeros_ip_tftp/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/tftp get [print show-ids]] terraform import routeros_ip_tftp.file "*0" ================================================ FILE: examples/resources/routeros_ip_tftp/resource.tf ================================================ resource "routeros_ip_tftp" "file" { ip_addresses = ["10.0.0.0/24"] req_filename = "file.txt" real_filename = routeros_file.file.name read_only = true } ================================================ FILE: examples/resources/routeros_ip_tftp_settings/import.sh ================================================ terraform import routeros_ip_tftp_settings.tftp_settings . ================================================ FILE: examples/resources/routeros_ip_tftp_settings/resource.tf ================================================ resource "routeros_ip_tftp_settings" "tftp_settings" { max_block_size = 4096 } ================================================ FILE: examples/resources/routeros_ip_traffic_flow/import.sh ================================================ terraform import routeros_ip_traffic_flow.test . ================================================ FILE: examples/resources/routeros_ip_traffic_flow/resource.tf ================================================ resource "routeros_ip_traffic_flow" "test" { packet_sampling = true sampling_interval = 2222 sampling_space = 1111 } ================================================ FILE: examples/resources/routeros_ip_traffic_flow_ipfix/import.sh ================================================ terraform import routeros_ip_traffic_flow_ipfix.test . ================================================ FILE: examples/resources/routeros_ip_traffic_flow_ipfix/resource.tf ================================================ resource "routeros_ip_traffic_flow_ipfix" "test" { nat_events = true dst_port = false } ================================================ FILE: examples/resources/routeros_ip_traffic_flow_target/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/traffic/flow/target get [print show-ids]] terraform import routeros_ip_traffic_flow_target.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ip_traffic_flow_target.test "dst_address=xxx" ================================================ FILE: examples/resources/routeros_ip_traffic_flow_target/resource.tf ================================================ resource "routeros_ip_traffic_flow_target" "test" { dst_address = "192.168.0.2" port = 2055 version = "9" } ================================================ FILE: examples/resources/routeros_ip_upnp/import.sh ================================================ terraform import routeros_ip_upnp.test . ================================================ FILE: examples/resources/routeros_ip_upnp/resource.tf ================================================ resource "routeros_ip_upnp" "test" { allow_disable_external_interface = true enabled = true show_dummy_rule = true } ================================================ FILE: examples/resources/routeros_ip_upnp_interfaces/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/upnp/interfaces get [print show-ids]] terraform import routeros_ip_upnp_interfaces.test '*1' #Or you can import a resource using one of its attributes terraform import routeros_ip_upnp_interfaces.test "name=xxx" ================================================ FILE: examples/resources/routeros_ip_upnp_interfaces/resource.tf ================================================ resource "routeros_ip_upnp_interfaces" "test" { interface = "ether1" type = "external" forced_external_ip = "0.0.0.0" } ================================================ FILE: examples/resources/routeros_ip_vrf/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ip/vrf get [print show-ids]] terraform import routeros_ip_vrf.test_vrf_a "*0" # or terraform import routeros_ip_vrf.test_vrf_a "vrf_1" # or terraform import routeros_ip_vrf.test_vrf_a `"comment=Custom routing"` ================================================ FILE: examples/resources/routeros_ip_vrf/resource.tf ================================================ resource "routeros_interface_veth" "veth1" { name = "veth1" } resource "routeros_interface_veth" "veth2" { name = "veth2" } resource "routeros_ip_vrf" "test_vrf_a" { disabled = true name = "vrf_1" comment = "Custom routing" interfaces = ["veth1", "veth2"] depends_on = [routeros_interface_veth.veth1, routeros_interface_veth.veth2] } ================================================ FILE: examples/resources/routeros_ipv6_address/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ipv6/address get [print show-ids]] terraform import routeros_ipv6_address.ipv6_address "*0" ================================================ FILE: examples/resources/routeros_ipv6_address/resource.tf ================================================ resource "routeros_ipv6_address" "ipv6_address" { address = "fd55::1/64" interface = "ether1" disabled = false } ================================================ FILE: examples/resources/routeros_ipv6_dhcp_client/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ipv6/dhcp-client/ get [print show-ids]] terraform import routeros_ipv6_dhcp_client.inet_provider "*1" #Or you can import a resource using one of its attributes terraform import routeros_ipv6_dhcp_client.inet_provider "name=xxx" ================================================ FILE: examples/resources/routeros_ipv6_dhcp_client/resource.tf ================================================ resource "routeros_ipv6_dhcp_client" "inet_provider" { pool_name = "pub-add-pool" interface = "ether1" add_default_route = true pool_prefix_length = 64 request = ["prefix"] disabled = false } resource "routeros_ipv6_dhcp_client" "client" { pool_name = "pub-add-pool" interface = "ether1" add_default_route = true pool_prefix_length = 64 request = ["prefix"] } ================================================ FILE: examples/resources/routeros_ipv6_dhcp_client_option/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ipv6/dhcp-client/option get [print show-ids]] terraform import routeros_ipv6_dhcp_client_option.option "*0" #Or you can import a resource using one of its attributes terraform import routeros_ipv6_dhcp_client_option.option "name=xxx" ================================================ FILE: examples/resources/routeros_ipv6_dhcp_client_option/resource.tf ================================================ resource "routeros_ipv6_dhcp_client_option" "option" { name = "my-dhcp-option" code = 60 } ================================================ FILE: examples/resources/routeros_ipv6_dhcp_server/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ipv6/dhcp/server get [print show-ids]] terraform import routeros_ipv6_dhcp_server.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ipv6_dhcp_server.test "name=test-dhcpv6" ================================================ FILE: examples/resources/routeros_ipv6_dhcp_server/resource.tf ================================================ resource "routeros_ipv6_pool" "pool-0" { name = "test-pool-0" prefix = "2001:db8:40::/48" prefix_length = 64 } resource "routeros_ipv6_dhcp_server" "test" { address_pool = routeros_ipv6_pool.pool-0.name interface = "bridge" lease_time = "1m" name = "test-dhcpv6" preference = 128 } ================================================ FILE: examples/resources/routeros_ipv6_dhcp_server_option/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ipv6/dhcp/server/option get [print show-ids]] terraform import routeros_ipv6_dhcp_server_option.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ipv6_dhcp_server_option.test "name=domain-search" ================================================ FILE: examples/resources/routeros_ipv6_dhcp_server_option/resource.tf ================================================ resource "routeros_ipv6_dhcp_server_option" "test" { name = "domain-search" code = 24 value = "0x07'example'0x05'local'0x00" } ================================================ FILE: examples/resources/routeros_ipv6_dhcp_server_option_sets/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ipv6/dhcp/server/option/sets get [print show-ids]] terraform import routeros_ipv6_dhcp_server_option_sets.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ipv6_dhcp_server_option_sets.test "name=test-set" ================================================ FILE: examples/resources/routeros_ipv6_dhcp_server_option_sets/resource.tf ================================================ resource "routeros_ipv6_dhcp_server_option" "domain-search" { name = "domain-search" code = 24 value = "0x07'example'0x05'local'0x00" } resource "routeros_ipv6_dhcp_server_option_sets" "test" { name = "test-set" options = [routeros_ipv6_dhcp_server_option.domain-search.name] } ================================================ FILE: examples/resources/routeros_ipv6_firewall_addr_list/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ipv6/firewall/address-list get [print show-ids]] terraform import routeros_ipv6_firewall_addr_list.example_list "*0" #Or you can import a resource using one of its attributes terraform import routeros_ipv6_firewall_addr_list.example_list "name=xxx" ================================================ FILE: examples/resources/routeros_ipv6_firewall_addr_list/resource.tf ================================================ resource "routeros_ipv6_firewall_addr_list" "example_list" { address = "123:dead:beaf::/64" list = "Example List" } ================================================ FILE: examples/resources/routeros_ipv6_firewall_filter/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ipv6/firewall/filter get [print show-ids]] terraform import routeros_ipv6_firewall_filter.rule "*0" #Or you can import a resource using one of its attributes terraform import routeros_ipv6_firewall_filter.rule "name=xxx" ================================================ FILE: examples/resources/routeros_ipv6_firewall_filter/resource.tf ================================================ resource "routeros_ipv6_firewall_filter" "rule" { action = "accept" chain = "forward" src_address = "2001:DB8:1000::1" dst_address = "2001:DB8:2000::1" dst_port = "443" protocol = "tcp" } ================================================ FILE: examples/resources/routeros_ipv6_firewall_mangle/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ipv6/firewall/mangle get [print show-ids]] terraform import routeros_ipv6_firewall_mangle.rule "*0" #Or you can import a resource using one of its attributes terraform import routeros_ipv6_firewall_mangle.rule "name=xxx" ================================================ FILE: examples/resources/routeros_ipv6_firewall_mangle/resource.tf ================================================ resource "routeros_ipv6_firewall_mangle" "rule" { action = "change-mss" chain = "forward" out_interface = "pppoe-out" protocol = "tcp" tcp_flags = "syn" new_mss = "1130" tcp_mss = "1301-65535" } ================================================ FILE: examples/resources/routeros_ipv6_firewall_nat/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ipv6/firewall/nat get [print show-ids]] terraform import routeros_ipv6_firewall_nat.rule "*0" #Or you can import a resource using one of its attributes terraform import routeros_ipv6_firewall_nat.rule "name=xxx" ================================================ FILE: examples/resources/routeros_ipv6_firewall_nat/resource.tf ================================================ resource "routeros_ipv6_firewall_nat" "rule" { action = "masquerade" chain = "srcnat" out_interface = "ether16" } ================================================ FILE: examples/resources/routeros_ipv6_nd_prefix/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ipv6/nd/prefix get [print show-ids]] terraform import routeros_ipv6_nd_prefix.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ipv6_nd_prefix.test "prefix=xxx" ================================================ FILE: examples/resources/routeros_ipv6_nd_prefix/resource.tf ================================================ resource "routeros_ipv6_nd_prefix" "test" { prefix = "fd55::/64" interface = "ether1" preferred_lifetime = "6d24h" } ================================================ FILE: examples/resources/routeros_ipv6_neighbor_discovery/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ipv6/nd get [print show-ids]] terraform import routeros_ipv6_neighbor_discovery.ndether1 "*0" #Or you can import a resource using one of its attributes terraform import routeros_ipv6_neighbor_discovery.ndether1 "name=xxx" ================================================ FILE: examples/resources/routeros_ipv6_neighbor_discovery/resource.tf ================================================ resource "routeros_ipv6_neighbor_discovery" "test" { interface = "ether1" hop_limit = 33 advertise_dns = false advertise_mac_address = true disabled = false managed_address_configuration = true mtu = 9000 other_configuration = true pref64_prefixes = [] ra_delay = "3s" ra_interval = "3m20s-10m" ra_lifetime = "30m" ra_preference = "high" reachable_time = "10m" retransmit_interval = "12m" } ================================================ FILE: examples/resources/routeros_ipv6_pool/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ipv6/pool get [print show-ids]] terraform import routeros_ipv6_pool.test *3 #Or you can import a resource using one of its attributes terraform import routeros_ipv6_pool.test "name=test-pool" ================================================ FILE: examples/resources/routeros_ipv6_pool/resource.tf ================================================ resource "routeros_ipv6_pool" "test" { name = "test-pool" prefix = "2001:db8:12::/48" prefix_length = 64 } ================================================ FILE: examples/resources/routeros_ipv6_route/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ipv6/route get [print show-ids]] terraform import routeros_ipv6_route.a_route "*0" #Or you can import a resource using one of its attributes terraform import routeros_ipv6_route.a_route "name=xxx" ================================================ FILE: examples/resources/routeros_ipv6_route/resource.tf ================================================ resource "routeros_ipv6_route" "a_route" { dst_address = "::/0" gateway = "2001:DB8:1000::1" } ================================================ FILE: examples/resources/routeros_ipv6_settings/import.sh ================================================ terraform import routeros_ipv6_settings.settings . ================================================ FILE: examples/resources/routeros_ipv6_settings/resource.tf ================================================ resource "routeros_ipv6_settings" "settings" { accept_redirects = "no" } ================================================ FILE: examples/resources/routeros_move_items/resource.tf ================================================ variable "rule" { type = list(object({ chain = string action = string connection_state = optional(string) in_interface_list = optional(string, "all") out_interface_list = optional(string) src_address = optional(string, "0.0.0.0/0") dst_address = optional(string) src_port = optional(string) dst_port = optional(string) protocol = optional(string) comment = optional(string, "(terraform-defined)") log = optional(bool, false) disabled = optional(bool, true) })) default = [ { chain = "input", action = "accept", comment = "00" }, { chain = "input", action = "accept", comment = "01" }, { chain = "input", action = "accept", comment = "02" }, { chain = "input", action = "accept", comment = "03" }, { chain = "input", action = "accept", comment = "04" }, { chain = "input", action = "accept", comment = "05" }, { chain = "input", action = "accept", comment = "06" }, { chain = "input", action = "accept", comment = "07" }, { chain = "input", action = "accept", comment = "08" }, { chain = "input", action = "accept", comment = "09" }, { chain = "input", action = "accept", comment = "10" }, { chain = "input", action = "accept", comment = "11" }, { chain = "input", action = "accept", comment = "12" }, { chain = "input", action = "accept", comment = "13" }, { chain = "input", action = "accept", comment = "14" }, { chain = "input", action = "accept", comment = "15" }, { chain = "input", action = "accept", comment = "16" }, { chain = "input", action = "accept", comment = "17" }, { chain = "input", action = "accept", comment = "18" }, { chain = "input", action = "accept", comment = "19" }, { chain = "input", action = "accept", comment = "20" }, { chain = "input", action = "accept", comment = "21" }, { chain = "input", action = "accept", comment = "22" }, { chain = "input", action = "accept", comment = "23" }, { chain = "input", action = "accept", comment = "24" }, { chain = "input", action = "accept", comment = "25" }, { chain = "input", action = "accept", comment = "26" }, { chain = "input", action = "accept", comment = "27" }, { chain = "input", action = "accept", comment = "28" }, { chain = "input", action = "accept", comment = "29" }, { chain = "input", action = "accept", comment = "30" }, { chain = "input", action = "accept", comment = "31" }, ] } locals { # https://discuss.hashicorp.com/t/does-map-sort-keys/12056/2 # Map keys are always iterated in lexicographical order! rule_map = { for idx, rule in var.rule : format("%03d", idx) => rule } } resource "routeros_ip_firewall_filter" "rules" { for_each = local.rule_map chain = each.value.chain action = each.value.action comment = each.value.comment log = each.value.log disabled = each.value.disabled connection_state = each.value.connection_state in_interface_list = each.value.in_interface_list src_address = each.value.src_address dst_port = each.value.dst_port protocol = each.value.protocol } resource "routeros_move_items" "fw_rules" { # resource_name = "routeros_ip_firewall_filter" resource_path = "/ip/firewall/filter" sequence = [for i, _ in local.rule_map : routeros_ip_firewall_filter.rules[i].id] depends_on = [routeros_ip_firewall_filter.rules] } ================================================ FILE: examples/resources/routeros_ovpn_server/import.sh ================================================ terraform import routeros_openvpn_server.server . ================================================ FILE: examples/resources/routeros_ovpn_server/resource.tf ================================================ resource "routeros_ip_pool" "ovpn-pool" { name = "ovpn-pool" ranges = ["192.168.77.2-192.168.77.254"] } resource "routeros_system_certificate" "ovpn_ca" { name = "OpenVPN-Root-CA" common_name = "OpenVPN Root CA" key_size = "prime256v1" key_usage = ["key-cert-sign", "crl-sign"] trusted = true sign { } } resource "routeros_system_certificate" "ovpn_server_crt" { name = "OpenVPN-Server-Certificate" common_name = "Mikrotik OpenVPN" key_size = "prime256v1" key_usage = ["digital-signature", "key-encipherment", "tls-server"] sign { ca = routeros_system_certificate.ovpn_ca.name } } resource "routeros_ppp_profile" "test" { name = "ovpn" local_address = "192.168.77.1" remote_address = "ovpn-pool" use_upnp = "no" } resource "routeros_ppp_secret" "test" { name = "user-test" password = "123" profile = routeros_ppp_profile.test.name } resource "routeros_ovpn_server" "server" { enabled = true certificate = routeros_system_certificate.ovpn_server_crt.name auth = ["sha256", "sha512"] tls_version = "only-1.2" default_profile = routeros_ppp_profile.test.name } # The resource should be created only after the OpenVPN server is enabled! resource "routeros_interface_ovpn_server" "user1" { name = "ovpn-in1" user = "user1" depends_on = [routeros_ovpn_server.server] } ================================================ FILE: examples/resources/routeros_ppp_aaa/import.sh ================================================ terraform import routeros_ppp_aaa.settings . ================================================ FILE: examples/resources/routeros_ppp_aaa/resource.tf ================================================ resource "routeros_ppp_aaa" "settings" { use_radius = true } ================================================ FILE: examples/resources/routeros_ppp_profile/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ppp/profile get [print show-ids]] terraform import routeros_ppp_profile.test *6 #Or you can import a resource using one of its attributes terraform import routeros_ppp_profile.test "name=xxx" ================================================ FILE: examples/resources/routeros_ppp_profile/resource.tf ================================================ resource "routeros_ppp_profile" "test" { name = "ovpn" local_address = "192.168.77.1" remote_address = "ovpn-pool" use_upnp = "no" } ================================================ FILE: examples/resources/routeros_ppp_secret/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/ppp/secret get [print show-ids]] terraform import routeros_ppp_secret.test *6 #Or you can import a resource using one of its attributes terraform import routeros_ppp_secret.test "name=xxx" ================================================ FILE: examples/resources/routeros_ppp_secret/resource.tf ================================================ resource "routeros_ppp_secret" "test" { name = "user-test" password = "123" profile = "default" } ================================================ FILE: examples/resources/routeros_queue_simple/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/queue/simple get [print show-ids]] terraform import routeros_queue_simple.test *3 #Or you can import a resource using one of its attributes terraform import routeros_queue_simple.test "name=server" ================================================ FILE: examples/resources/routeros_queue_simple/resource.tf ================================================ resource "routeros_queue_simple" "test" { name = "server" target = ["10.1.1.1/32"] max_limit = "0/0" } ================================================ FILE: examples/resources/routeros_queue_tree/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/queue/tree get [print show-ids]] terraform import routeros_queue_tree.test *1000000 #Or you can import a resource using one of its attributes terraform import routeros_queue_tree.test "name=server" ================================================ FILE: examples/resources/routeros_queue_tree/resource.tf ================================================ resource "routeros_queue_tree" "test" { name = "server" parent = "global" max_limit = "10M" packet_mark = ["pmark-server"] } ================================================ FILE: examples/resources/routeros_queue_type/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/queue/type get [print show-ids]] terraform import routeros_queue_type.test *3 #Or you can import a resource using one of its attributes terraform import routeros_queue_type.test "name=pcq-test" ================================================ FILE: examples/resources/routeros_queue_type/resource.tf ================================================ resource "routeros_queue_type" "test" { name = "pcq-test" kind = "pcq" pcq_rate = 0 pcq_limit = 50 pcq_classifier = ["dst-address"] } ================================================ FILE: examples/resources/routeros_radius/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/radius get [print show-ids]] terraform import routeros_radius.user_manager *1 #Or you can import a resource using one of its attributes terraform import routeros_radius.user_manager "name=xxx" ================================================ FILE: examples/resources/routeros_radius/resource.tf ================================================ resource "routeros_radius" "user_manager" { address = "127.0.0.1" service = ["ppp", "login"] } ================================================ FILE: examples/resources/routeros_radius_incoming/import.sh ================================================ terraform import routeros_radius_incoming.settings . ================================================ FILE: examples/resources/routeros_radius_incoming/resource.tf ================================================ resource "routeros_radius_incoming" "settings" { accept = true } ================================================ FILE: examples/resources/routeros_routing_bfd_configuration/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/routing/bfd/configuration get [print show-ids]] terraform import routeros_routing_bfd_configuration.test *3 ================================================ FILE: examples/resources/routeros_routing_bfd_configuration/resource.tf ================================================ resource "routeros_routing_bfd_configuration" "test" { interfaces = ["lo", "ether2"] vrf = "main" forbid_bfd = true } ================================================ FILE: examples/resources/routeros_routing_bgp_connection/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/routing/bgp/connection get [print show-ids]] terraform import routeros_routing_bgp_connection.test *3 ================================================ FILE: examples/resources/routeros_routing_bgp_connection/resource.tf ================================================ resource "routeros_routing_bgp_connection" "test" { name = "neighbor-test" as = "65550/5" as_override = true add_path_out = "none" remote { address = "172.17.0.1" as = "12345/5" } local { role = "ebgp" } } ================================================ FILE: examples/resources/routeros_routing_bgp_evpn/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/routing/bgp/evpn get [print show-ids]] terraform import routeros_routing_bgp_evpn.test *3 #Or you can import a resource using one of its attributes terraform import routeros_routing_bgp_evpn.test "name=xxx" ================================================ FILE: examples/resources/routeros_routing_bgp_evpn/resource.tf ================================================ resource "routeros_routing_bgp_instance" "test" { as = "65000" name = "bgp-instance-1" } resource "routeros_routing_bgp_evpn" "test" { disabled = false export { route_targets = ["1010:1010"] } import { route_targets = ["1010:1010"] } instance = resource.routeros_routing_bgp_instance.test.name name = "bgp-evpn-1" vni = 1010 } ================================================ FILE: examples/resources/routeros_routing_bgp_instance/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/routing/bgp/instance get [print show-ids]] terraform import routeros_routing_bgp_instance.test *3 #Or you can import a resource using one of its attributes terraform import routeros_routing_bgp_instance.test "name=xxx" ================================================ FILE: examples/resources/routeros_routing_bgp_instance/resource.tf ================================================ resource "routeros_routing_bgp_instance" "test" { } ================================================ FILE: examples/resources/routeros_routing_bgp_template/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/routing/bgp/template get [print show-ids]] terraform import routeros_routing_bgp_template.test *3 ================================================ FILE: examples/resources/routeros_routing_bgp_template/resource.tf ================================================ resource "routeros_routing_bgp_template" "test" { name = "test-template" as = 65521 input { limit_process_routes_ipv4 = 5 limit_process_routes_ipv6 = 5 } output { affinity = "alone" keep_sent_attributes = true default_originate = "never" } // save_to = "bgp.dump" } ================================================ FILE: examples/resources/routeros_routing_bgp_vpn/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/routing/bgp/vpn get [print show-ids]] terraform import routeros_routing_bgp_vpn.test *3 #Or you can import a resource using one of its attributes terraform import routeros_routing_bgp_vpn.test "name=xxx" ================================================ FILE: examples/resources/routeros_routing_bgp_vpn/resource.tf ================================================ resource "routeros_routing_bgp_vpn" "test" { disabled = false export { redistribute = "connected" route_targets = ["1:1"] } import { route_targets = ["1:2"] } label_allocation_policy = "per-vrf" name = "bgp-mpls-vpn-test" route_distinguisher = "1.2.3.4:1" vrf = "vrfTest1" } ================================================ FILE: examples/resources/routeros_routing_filter_rule/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> /routing/filter/rule/print show-ids terraform import routeros_routing_filter_rule.test "*0" #Or you can import a resource using one of its attributes terraform import routeros_routing_filter_rule.test "name=xxx" ================================================ FILE: examples/resources/routeros_routing_filter_rule/resource.tf ================================================ resource "routeros_routing_filter_rule" "test" { chain = "testChain" rule = "if (dst in 192.168.1.0/24 && dst-len>24) {set distance +1; accept} else {set distance -1; accept}" comment = "comment" disabled = true } ================================================ FILE: examples/resources/routeros_routing_id/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/routing/id get [print show-ids]] terraform import routeros_routing_id.test "*1" #Or you can import a resource using one of its attributes terraform import routeros_routing_id.test "name=router-id-test" ================================================ FILE: examples/resources/routeros_routing_id/resource.tf ================================================ resource "routeros_routing_id" "test" { name = "router-id-test" router_id = "10.10.10.10" select_dynamic_id = "any" } ================================================ FILE: examples/resources/routeros_routing_igmp_proxy_interface/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/routing/igmp/proxy/interface get [print show-ids]] terraform import routeros_routing_igmp_proxy_interface.test *3 #Or you can import a resource using one of its attributes terraform import routeros_routing_igmp_proxy_interface.test "interface=xxx" ================================================ FILE: examples/resources/routeros_routing_igmp_proxy_interface/resource.tf ================================================ resource "routeros_routing_igmp_proxy_interface" "test" { alternative_subnets = ["0.0.0.1/32", "0.0.0.2/32"] disabled = true interface = "lo" threshold = 5 } ================================================ FILE: examples/resources/routeros_routing_ospf_area/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> /routing/ospf/area/print show-ids terraform import routeros_routing_ospf_area.test_routing_ospf_area "*0" #Or you can import a resource using one of its attributes terraform import routeros_routing_ospf_area.test_routing_ospf_area "name=xxx" ================================================ FILE: examples/resources/routeros_routing_ospf_area/resource.tf ================================================ resource "routeros_routing_ospf_instance" "test_routing_ospf_instance" { name = "test_routing_ospf_instance" } resource "routeros_routing_ospf_area" "test_routing_ospf_area" { name = "test_routing_ospf_area" instance = routeros_routing_ospf_instance.test_routing_ospf_instance.name } ================================================ FILE: examples/resources/routeros_routing_ospf_area_range/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/routing/ospf/area/range get [print show-ids]] terraform import routeros_routing_ospf_area_range.test *3 #Or you can import a resource using one of its attributes terraform import routeros_routing_ospf_area_range.test "comment=xxx" ================================================ FILE: examples/resources/routeros_routing_ospf_area_range/resource.tf ================================================ resource "routeros_routing_ospf_area_range" "test" { area = routeros_routing_ospf_area.ospf-area-1.name advertise = true prefix = "::/64" disabled = true } ================================================ FILE: examples/resources/routeros_routing_ospf_instance/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> /routing/ospf/instance/print show-ids terraform import routeros_routing_ospf_instance.test_routing_ospf_instance "*0" #Or you can import a resource using one of its attributes terraform import routeros_routing_ospf_instance.test_routing_ospf_instance "name=xxx" ================================================ FILE: examples/resources/routeros_routing_ospf_instance/resource.tf ================================================ resource "routeros_routing_ospf_instance" "test_routing_ospf_instance" { name = "test_routing_ospf_instance" } ================================================ FILE: examples/resources/routeros_routing_ospf_interface_template/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> /routing/ospf/interface-template/print show-ids terraform import routeros_routing_ospf_interface_template.test_routing_ospf_interface_template "*0" #Or you can import a resource using one of its attributes terraform import routeros_routing_ospf_interface_template.test_routing_ospf_interface_template "name=xxx" ================================================ FILE: examples/resources/routeros_routing_ospf_interface_template/resource.tf ================================================ resource "routeros_routing_ospf_instance" "test_routing_ospf_instance" { name = "test_routing_ospf_instance" } resource "routeros_routing_ospf_area" "test_routing_ospf_area" { name = "test_routing_ospf_area" instance = routeros_routing_ospf_instance.test_routing_ospf_instance.name } resource "routeros_routing_ospf_interface_template" "test_routing_ospf_interface_template" { area = routeros_routing_ospf_area.test_routing_ospf_area.name } ================================================ FILE: examples/resources/routeros_routing_rule/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/routing/rule get [print show-ids]] terraform import routeros_routing_rule.test *3 #Or you can import a resource using one of its attributes terraform import routeros_routing_rule.test "name=xxx" ================================================ FILE: examples/resources/routeros_routing_rule/resource.tf ================================================ resource "routeros_routing_rule" "test" { dst_address = "192.168.1.0/24" action = "lookup-only-in-table" interface = "ether1" } ================================================ FILE: examples/resources/routeros_routing_table/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/routing/table get [print show-ids]] terraform import routeros_routing_table.test_table "*0" #Or you can import a resource using one of its attributes terraform import routeros_routing_table.test_table "name=xxx" ================================================ FILE: examples/resources/routeros_routing_table/resource.tf ================================================ resource "routeros_routing_table" "test_table" { name = "to_ISP1" fib = false } ================================================ FILE: examples/resources/routeros_snmp/import.sh ================================================ terraform import routeros_snmp.test . ================================================ FILE: examples/resources/routeros_snmp/resource.tf ================================================ resource "routeros_snmp" "test" { contact = "John D." enabled = true engine_id_suffix = "8a3c" location = "Backyard" trap_community = "private" trap_generators = "start-trap" trap_version = 3 } ================================================ FILE: examples/resources/routeros_snmp_community/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/snmp/community get [print show-ids]] terraform import routeros_snmp_community.test "*0" #Or you can import a resource using one of its attributes terraform import routeros_snmp_community.test "name=xxx" ================================================ FILE: examples/resources/routeros_snmp_community/resource.tf ================================================ resource "routeros_snmp_community" "test" { authentication_password = "authpasswd" authentication_protocol = "MD5" comment = "Comment" disabled = true encryption_password = "encpassword" encryption_protocol = "DES" name = "private" read_access = true security = "private" write_access = true } resource "routeros_snmp_community" "mything" { addresses = ["10.0.1.12", "10.10.0.129"] name = "mything" } ================================================ FILE: examples/resources/routeros_system_certificate/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/certificate get [print show-ids]] #If you plan to manipulate the certificate requiring signing, you need to correctly fill in the sign{} section. #Changes in the sign{} section will not cause changes in the certificate. It's not a bug, it's a feature! terraform import routeros_system_certificate.client *9D #Or you can import a resource using one of its attributes terraform import routeros_system_certificate.client "name=xxx" ================================================ FILE: examples/resources/routeros_system_certificate/resource.tf ================================================ resource "routeros_system_certificate" "root_ca" { name = "Test-Root-CA" common_name = "RootCA" key_usage = ["key-cert-sign", "crl-sign"] trusted = true # Sign Root CA. sign { } } # digitalSignature: Used for entity and data origin authentication with integrity. # keyEncipherment: Used to encrypt symmetric key, which is then transferred to target. # keyAgreement: Enables use of key agreement to establish symmetric key with target. resource "routeros_system_certificate" "server_crt" { name = "Server-Certificate" common_name = "server.crt" # KUs: igitalSignature, keyEncipherment or keyAgreement key_usage = ["digital-signature", "key-encipherment", "tls-server"] sign { ca = routeros_system_certificate.root_ca.name } } resource "routeros_system_certificate" "client_crt" { name = "Client-Certificate" common_name = "client.crt" key_size = "prime256v1" # KUs: digitalSignature and/or keyAgreement key_usage = ["digital-signature", "key-agreement", "tls-client"] sign { ca = routeros_system_certificate.root_ca.name } } resource "routeros_system_certificate" "unsigned_crt" { name = "Unsigned-Certificate" common_name = "unsigned.crt" key_size = "1024" subject_alt_name = "DNS:router.lan,DNS:myrouter.lan,IP:192.168.88.1" } resource "routeros_system_certificate" "scep_client" { name = "SCEP-Client" common_name = "scep-client.crt" key_usage = ["digital-signature", "key-agreement", "tls-client"] sign_via_scep { scep_url = "http://scep.server/scep/test" } } # Import certificate data "routeros_x509" "cert" { data = < /certificate/scep-server/print show-ids terraform import routeros_system_certificate_scep_server.example_scep_server "*1" #Or you can import a resource using one of its attributes terraform import routeros_system_certificate_scep_server.example_scep_server "name=xxx" ================================================ FILE: examples/resources/routeros_system_certificate_scep_server/resource.tf ================================================ resource "routeros_system_certificate" "example_root_ca" { name = "example_root_ca" common_name = "Example Root CA" key_usage = ["key-cert-sign", "crl-sign"] trusted = true sign { } } # You can also use the alias "routeros_certificate_scep_server" resource "routeros_system_certificate_scep_server" "example_scep_server" { ca_cert = routeros_system_certificate.example_root_ca.name path = "/scep/example_scep_server" days_valid = 30 } ================================================ FILE: examples/resources/routeros_system_clock/import.sh ================================================ terraform import routeros_system_clock.set . ================================================ FILE: examples/resources/routeros_system_clock/resource.tf ================================================ resource "routeros_system_clock" "set" { date = "2024-05-15" time = "17:58:11" time_zone_name = "EST" } ================================================ FILE: examples/resources/routeros_system_identity/import.sh ================================================ terraform import routeros_system_identity.identity . ================================================ FILE: examples/resources/routeros_system_identity/resource.tf ================================================ resource "routeros_system_identity" "identity" { name = "My Router" } ================================================ FILE: examples/resources/routeros_system_led/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/system/leds get [print show-ids]] terraform import routeros_system_led.sfp1 '*1' #Or you can import a resource using one of its attributes terraform import routeros_system_led.sfp1 "name=xxx" ================================================ FILE: examples/resources/routeros_system_led/resource.tf ================================================ resource "routeros_system_led" "sfp1" { interface = "sfp1" leds = ["sfp-led"] type = "interface-activity" } ================================================ FILE: examples/resources/routeros_system_led_settings/import.sh ================================================ terraform import routeros_system_led_settings.settings . ================================================ FILE: examples/resources/routeros_system_led_settings/resource.tf ================================================ resource "routeros_system_led_settings" "settings" { all_leds_off = "immediate" } ================================================ FILE: examples/resources/routeros_system_logging/import.sh ================================================ # The ID can be found via API or the terminal # The command for the terminal is -> :put [/system/logging get [print show-ids]] terraform import routeros_system_logging.log_snmp_disk "*4" ================================================ FILE: examples/resources/routeros_system_logging/resource.tf ================================================ resource "routeros_system_logging" "log_snmp_disk" { action = "disk" topics = ["snmp"] } ================================================ FILE: examples/resources/routeros_system_logging_actions/import.sh ================================================ # The ID can be found via API or the terminal # The command for the terminal is -> :put [/system/logging/action get [print show-ids]] terraform import routeros_system_logging_action.disk "*1" ================================================ FILE: examples/resources/routeros_system_logging_actions/resource.tf ================================================ resource "routeros_system_logging_action" "disk" { name = "disk" target = "remote" remote = "192.168.1.1" bsd_syslog = true syslog_facility = "user" syslog_severity = "notice" syslog_time_format = "iso8601" } ================================================ FILE: examples/resources/routeros_system_note/import.sh ================================================ terraform import routeros_system_note.test . ================================================ FILE: examples/resources/routeros_system_note/resource.tf ================================================ resource "routeros_system_note" "test" { note = "For authorized use only." show_at_login = true show_at_cli_login = true } ================================================ FILE: examples/resources/routeros_system_ntp_client/import.sh ================================================ terraform import routeros_system_ntp_client.test . ================================================ FILE: examples/resources/routeros_system_ntp_client/resource.tf ================================================ resource "routeros_system_ntp_client" "test" { enabled = true mode = "unicast" servers = ["146.59.35.38", "167.235.201.139"] } ================================================ FILE: examples/resources/routeros_system_ntp_server/import.sh ================================================ terraform import routeros_system_ntp_server.test . ================================================ FILE: examples/resources/routeros_system_ntp_server/resource.tf ================================================ resource "routeros_system_ntp_server" "test" { enabled = true broadcast = true multicast = true manycast = true use_local_clock = true local_clock_stratum = 3 } ================================================ FILE: examples/resources/routeros_system_routerboard_button_mode/import.sh ================================================ terraform import routeros_system_routerboard_button_mode.settings . ================================================ FILE: examples/resources/routeros_system_routerboard_button_mode/resource.tf ================================================ resource "routeros_system_script" "mode_button" { name = "mode-button" source = < :put [/system/scheduler get [print show-ids]] terraform import routeros_system_scheduler.schedule1 "*0" #Or you can import a resource using one of its attributes terraform import routeros_system_scheduler.schedule1 "name=xxx" ================================================ FILE: examples/resources/routeros_system_scheduler/resource.tf ================================================ resource "routeros_system_scheduler" "schedule1" { name = "schedule1" on_event = "script name" } ================================================ FILE: examples/resources/routeros_system_script/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/system/script get [print show-ids]] terraform import routeros_system_script.script "*0" #Or you can import a resource using one of its attributes terraform import routeros_system_script.script "name=xxx" ================================================ FILE: examples/resources/routeros_system_script/resource.tf ================================================ resource "routeros_system_script" "script" { name = "my_script" source = < :put [/user get [print show-ids]] terraform import routeros_system_user.test *1 #Or you can import a resource using one of its attributes terraform import routeros_system_user.test "name=xxx" ================================================ FILE: examples/resources/routeros_system_user/resource.tf ================================================ resource "routeros_system_user" "test" { name = "test-user-1" address = "0.0.0.0/0" group = "read" password = "secret" comment = "Test User" } ================================================ FILE: examples/resources/routeros_system_user_aaa/import.sh ================================================ terraform import routeros_system_user_aaa.settings . ================================================ FILE: examples/resources/routeros_system_user_aaa/resource.tf ================================================ resource "routeros_system_user_aaa" "settings" { use_radius = true } ================================================ FILE: examples/resources/routeros_system_user_group/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/user/group get [print show-ids]] terraform import routeros_system_user_group.terraform *1 #Or you can import a resource using one of its attributes terraform import routeros_system_user_group.terraform "name=xxx" ================================================ FILE: examples/resources/routeros_system_user_group/resource.tf ================================================ resource "routeros_system_user_group" "terraform" { name = "terraform" policy = ["api", "!ftp", "!local", "password", "policy", "read", "!reboot", "!rest-api", "!romon", "sensitive", "!sniff", "!ssh", "!telnet", "!test", "!web", "!winbox", "write"] } ================================================ FILE: examples/resources/routeros_system_user_settings/import.sh ================================================ terraform import routeros_system_user_settings.settings . ================================================ FILE: examples/resources/routeros_system_user_settings/resource.tf ================================================ resource "routeros_system_user_settings" "settings" { minimum_categories = 2 minimum_password_length = 8 } ================================================ FILE: examples/resources/routeros_system_user_sshkeys/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/user/ssh-keys get [print show-ids]] terraform import routeros_system_user_sshkeys.test *1 #Or you can import a resource using one of its attributes terraform import routeros_system_user_sshkeys.test "name=xxx" ================================================ FILE: examples/resources/routeros_system_user_sshkeys/resource.tf ================================================ resource "routeros_system_user_sshkeys" "test" { user = "test-user-1" key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCyJ1EvW98veNVzR3VamNgmu0xOd/JK9YNvP/pa4WC5eT90UbX4TN7dKEK/x2FCwnnG9u0FQhzG2qa/Cg8meUvlfydn6uxc0/WCeXTKSu6sT63noPO6m4fHY7gu3Zt+fOc/WYGch9sBeWjZlCS1mA2lajkWhM3J8TFWCFm2Zk4/S3s5mt6VLbwpQnH2LhE41+azzDEVhcR6i3FfdgOF/J+j2fYYHJsBEKoQA5zUac2zWmz7X4Rv/g11ZBRqdMpHSD58o5F9lBb13antu5GcEs5RXpXp08OyXuRV9qhFpDBC8DOMALSOgT3vnu8uJLgo8QIulERofj/cRXbLCsmvMbpioBuGFXWx3ha4Ntd6z07kUh2KVbaIQLd/629UHNvgIhoBLlREJ8E5vllsX+jh8hRITHcCiEwXcDO+gG3hvJt0+jm8S8SObE/IHk8VuwWdhIsSku5vd+wVlxm8VeJzjc0cjdIiytvsq8VpLudKEUiqR0f2tHcoq8H+xcJv3Ycu1i8=" comment = "Test User" } ================================================ FILE: examples/resources/routeros_tool_bandwidth_server/import.sh ================================================ terraform import routeros_tool_bandwidth_server.test . ================================================ FILE: examples/resources/routeros_tool_bandwidth_server/resource.tf ================================================ resource "routeros_tool_bandwidth_server" "test" { enabled = true authenticate = false max_sessions = 100 allocate_udp_ports_from = 2000 } ================================================ FILE: examples/resources/routeros_tool_graphing_interface/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/tool/graphing/interface get [print show-ids]] terraform import routeros_tool_graphing_interface.test "*0" ================================================ FILE: examples/resources/routeros_tool_graphing_interface/resource.tf ================================================ resource "routeros_tool_graphing_interface" "test" { interface = "all" } ================================================ FILE: examples/resources/routeros_tool_graphing_queue/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/tool/graphing/queue get [print show-ids]] terraform import routeros_tool_graphing_queue.test "*0" ================================================ FILE: examples/resources/routeros_tool_graphing_queue/resource.tf ================================================ resource "routeros_tool_graphing_queue" "test" { simple_queue = "all" } ================================================ FILE: examples/resources/routeros_tool_graphing_resource/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/tool/graphing/resource get [print show-ids]] terraform import routeros_tool_graphing_resource.test "*0" ================================================ FILE: examples/resources/routeros_tool_graphing_resource/resource.tf ================================================ resource "routeros_tool_graphing_resource" "test" {} ================================================ FILE: examples/resources/routeros_tool_mac_server/import.sh ================================================ terraform import routeros_tool_mac_server.test . ================================================ FILE: examples/resources/routeros_tool_mac_server/resource.tf ================================================ resource "routeros_tool_mac_server" "test" { allowed_interface_list = "LAN" } ================================================ FILE: examples/resources/routeros_tool_mac_server_ping/import.sh ================================================ terraform import routeros_tool_mac_server_ping.test . ================================================ FILE: examples/resources/routeros_tool_mac_server_ping/resource.tf ================================================ resource "routeros_tool_mac_server_ping" "test" { enabled = false } ================================================ FILE: examples/resources/routeros_tool_mac_server_winbox/import.sh ================================================ terraform import routeros_tool_mac_server_winbox.test . ================================================ FILE: examples/resources/routeros_tool_mac_server_winbox/resource.tf ================================================ resource "routeros_tool_mac_server_winbox" "test" { allowed_interface_list = "LAN" } ================================================ FILE: examples/resources/routeros_tool_netwatch/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/tool/netwatch get [print show-ids]] terraform import routeros_tool_netwatch.test *3 #Or you can import a resource using one of its attributes terraform import routeros_tool_netwatch.test "name=xxx" ================================================ FILE: examples/resources/routeros_tool_netwatch/resource.tf ================================================ resource "routeros_tool_netwatch" "test" { name = "watch-google-pdns" type = "icmp" host = "8.8.8.8" interval = "30s" up_script = ":log info \"Ping to 8.8.8.8 successful\"" thr_max = "400ms" } ================================================ FILE: examples/resources/routeros_tool_sniffer/import.sh ================================================ terraform import routeros_tool_sniffer.test . ================================================ FILE: examples/resources/routeros_tool_sniffer/resource.tf ================================================ resource "routeros_tool_sniffer" "test" { enabled = true streaming_enabled = true streaming_server = "192.168.88.5:37008" filter_stream = true filter_interface = ["ether2"] filter_direction = "rx" filter_operator_between_entries = "and" } ================================================ FILE: examples/resources/routeros_user_manager_advanced/import.sh ================================================ terraform import routeros_user_manager_advanced.settings . ================================================ FILE: examples/resources/routeros_user_manager_advanced/resource.tf ================================================ resource "routeros_user_manager_advanced" "settings" { web_private_password = "password" web_private_username = "admin" } ================================================ FILE: examples/resources/routeros_user_manager_attribute/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/user-manager/attribute get [print show-ids]] terraform routeros_user_manager_attribute.mikrotik_wireless_comment '*1' #Or you can import a resource using one of its attributes terraform import routeros_user_manager_attribute.mikrotik_wireless_comment "name=xxx" ================================================ FILE: examples/resources/routeros_user_manager_attribute/resource.tf ================================================ resource "routeros_user_manager_attribute" "mikrotik_wireless_comment" { name = "Mikrotik-Wireless-Comment" packet_types = ["access-accept"] type_id = 21 value_type = "string" } ================================================ FILE: examples/resources/routeros_user_manager_database/import.sh ================================================ terraform import routeros_user_manager_database.settings . ================================================ FILE: examples/resources/routeros_user_manager_database/resource.tf ================================================ resource "routeros_user_manager_database" "settings" { db_path = "/flash/user-manager5" } ================================================ FILE: examples/resources/routeros_user_manager_limitation/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/user-manager/limitation get [print show-ids]] terraform import routeros_user_manager_limitation.test '*1' #Or you can import a resource using one of its attributes terraform import routeros_user_manager_limitation.test "name=xxx" ================================================ FILE: examples/resources/routeros_user_manager_limitation/resource.tf ================================================ resource "routeros_user_manager_limitation" "test" { name = "test" download_limit = 1024 upload_limit = 1024 uptime_limit = "10d" } ================================================ FILE: examples/resources/routeros_user_manager_profile/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/user-manager/profile get [print show-ids]] terraform import routeros_user_manager_profile.test '*1' #Or you can import a resource using one of its attributes terraform import routeros_user_manager_profile.test "name=xxx" ================================================ FILE: examples/resources/routeros_user_manager_profile/resource.tf ================================================ resource "routeros_user_manager_profile" "test" { name = "test" name_for_users = "Test" price = 0.02 } ================================================ FILE: examples/resources/routeros_user_manager_profile_limitation/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/user-manager/profile-limitation get [print show-ids]] terraform import routeros_user_manager_profile_limitation.weekend_night '*1' #Or you can import a resource using one of its attributes terraform import routeros_user_manager_profile_limitation.weekend_night "name=xxx" ================================================ FILE: examples/resources/routeros_user_manager_profile_limitation/resource.tf ================================================ resource "routeros_user_manager_limitation" "test" { name = "test" download_limit = 1024 upload_limit = 1024 uptime_limit = "10d" } resource "routeros_user_manager_profile" "test" { name = "test" name_for_users = "Test" price = 0.02 } resource "routeros_user_manager_profile_limitation" "weekend_night" { limitation = routeros_user_manager_limitation.test.name profile = routeros_user_manager_profile.test.name from_time = "0s" till_time = "6h" weekdays = [ "sunday", "saturday", ] } ================================================ FILE: examples/resources/routeros_user_manager_router/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/user-manager/router get [print show-ids]] terraform import routeros_user_manager_router.test '*1' #Or you can import a resource using one of its attributes terraform import routeros_user_manager_router.test "name=xxx" ================================================ FILE: examples/resources/routeros_user_manager_router/resource.tf ================================================ resource "routeros_user_manager_router" "test" { address = "127.0.0.1" name = "test" shared_secret = "password" } ================================================ FILE: examples/resources/routeros_user_manager_settings/import.sh ================================================ terraform import routeros_user_manager_settings.settings . ================================================ FILE: examples/resources/routeros_user_manager_settings/resource.tf ================================================ resource "routeros_user_manager_settings" "settings" { enabled = true } ================================================ FILE: examples/resources/routeros_user_manager_user/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/user-manager/user get [print show-ids]] terraform import routeros_user_manager_user.test '*1' #Or you can import a resource using one of its attributes terraform import routeros_user_manager_user.test "name=xxx" ================================================ FILE: examples/resources/routeros_user_manager_user/resource.tf ================================================ resource "routeros_user_manager_attribute" "mikrotik_wireless_comment" { name = "Mikrotik-Wireless-Comment" type_id = 21 value_type = "string" } resource "routeros_user_manager_attribute" "mikrotik_wireless_vlanid" { name = "Mikrotik-Wireless-VLANID" type_id = 26 value_type = "uint32" } resource "routeros_user_manager_user_group" "test" { name = "test" } resource "routeros_user_manager_user" "test" { attributes = [ "${routeros_user_manager_attribute.mikrotik_wireless_comment.name}:Test Group", "${routeros_user_manager_attribute.mikrotik_wireless_vlanid.name}:100", ] group = routeros_user_manager_user_group.test.name name = "test" password = "password" } ================================================ FILE: examples/resources/routeros_user_manager_user_group/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/user-manager/user/group get [print show-ids]] terraform import routeros_user_manager_user_group.test '*1' #Or you can import a resource using one of its attributes terraform import routeros_user_manager_user_group.test "name=xxx" ================================================ FILE: examples/resources/routeros_user_manager_user_group/resource.tf ================================================ resource "routeros_user_manager_attribute" "mikrotik_wireless_comment" { name = "Mikrotik-Wireless-Comment" type_id = 21 value_type = "string" } resource "routeros_user_manager_attribute" "mikrotik_wireless_vlanid" { name = "Mikrotik-Wireless-VLANID" type_id = 26 value_type = "uint32" } resource "routeros_user_manager_user_group" "test" { name = "test" attributes = [ "${routeros_user_manager_attribute.mikrotik_wireless_comment.name}:Test Group", "${routeros_user_manager_attribute.mikrotik_wireless_vlanid.name}:100", ] inner_auths = [ "ttls-chap", "ttls-pap", ] outer_auths = [ "chap", "pap", ] } ================================================ FILE: examples/resources/routeros_user_manager_user_profile/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/user-manager/user-profile get [print show-ids]] terraform import routeros_user_manager_user_profile.test '*1' #Or you can import a resource using one of its attributes terraform import routeros_user_manager_user_profile.test "name=xxx" ================================================ FILE: examples/resources/routeros_user_manager_user_profile/resource.tf ================================================ resource "routeros_user_manager_profile" "test" { name = "test" } resource "routeros_user_manager_user" "test" { name = "test" } resource "routeros_user_manager_user_profile" "test" { profile = routeros_user_manager_profile.test.name user = routeros_user_manager_user.test.name } ================================================ FILE: examples/resources/routeros_wifi/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wifi get [print show-ids]] terraform import routeros_wifi.wifi1 '*1' ================================================ FILE: examples/resources/routeros_wifi/resource.tf ================================================ # If you need to add a reference to an existing configuration, each inline section contains a `config` parameter # where you can specify the name of the actual resource. # configuration = { # config = routeros_wifi_configuration.my-config.name # } resource "routeros_wifi" "wifi1" { configuration = { manager = "capsman" } name = "wifi1" } ================================================ FILE: examples/resources/routeros_wifi_aaa/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wifi/aaa get [print show-ids]] terraform import routeros_wifi_aaa.aaa1 '*1' #Or you can import a resource using one of its attributes terraform import routeros_wifi_aaa.aaa1 "name=xxx" ================================================ FILE: examples/resources/routeros_wifi_aaa/resource.tf ================================================ resource "routeros_wifi_aaa" "aaa1" { called_format = "S" name = "aaa1" password_format = "" username_format = "AA:AA:AA:AA:AA:AA" } ================================================ FILE: examples/resources/routeros_wifi_access_list/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wifi/access-list get [print show-ids]] terraform import routeros_wifi_access_list.radius '*1' #Or you can import a resource using one of its attributes terraform import routeros_wifi_access_list.radius "name=xxx" ================================================ FILE: examples/resources/routeros_wifi_access_list/resource.tf ================================================ resource "routeros_wifi_access_list" "radius" { action = "query-radius" comment = "RADIUS" radius_accounting = true } ================================================ FILE: examples/resources/routeros_wifi_cap/import.sh ================================================ terraform import routeros_wifi_cap.settings . ================================================ FILE: examples/resources/routeros_wifi_cap/resource.tf ================================================ resource "routeros_wifi_cap" "settings" { enabled = true discovery_interfaces = ["bridge1"] } ================================================ FILE: examples/resources/routeros_wifi_capsman/import.sh ================================================ terraform import routeros_wifi_capsman.settings . ================================================ FILE: examples/resources/routeros_wifi_capsman/resource.tf ================================================ resource "routeros_wifi_capsman" "settings" { enabled = true interfaces = ["bridge1"] upgrade_policy = "suggest-same-version" } ================================================ FILE: examples/resources/routeros_wifi_channel/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wifi/channel get [print show-ids]] terraform import routeros_wifi_channel.channel1 '*1' ================================================ FILE: examples/resources/routeros_wifi_channel/resource.tf ================================================ resource "routeros_wifi_channel" "channel1" { name = "1" band = "2ghz-n" frequency = [2412] secondary_frequency = ["disabled"] skip_dfs_channels = "disabled" width = "20mhz" } ================================================ FILE: examples/resources/routeros_wifi_configuration/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wifi/configuration get [print show-ids]] terraform import routeros_wifi_configuration.configuration1 '*1' #Or you can import a resource using one of its attributes terraform import routeros_wifi_configuration.configuration1 "name=xxx" ================================================ FILE: examples/resources/routeros_wifi_configuration/resource.tf ================================================ resource "routeros_wifi_aaa" "aaa1" { called_format = "S" name = "aaa1" password_format = "" username_format = "AA:AA:AA:AA:AA:AA" } resource "routeros_wifi_channel" "channel1" { name = "1" band = "2ghz-n" frequency = [2412] secondary_frequency = ["disabled"] skip_dfs_channels = "disabled" width = "20mhz" } resource "routeros_wifi_datapath" "datapath1" { name = "datapath1" bridge = "bridge1" client_isolation = false } resource "routeros_wifi_security" "security1" { name = "security1" authentication_types = ["wpa2-psk", "wpa3-psk"] ft = true ft_preserve_vlanid = true passphrase = "password" } resource "routeros_wifi_configuration" "configuration1" { country = "Netherlands" manager = "capsman" mode = "ap" name = "configuration1" ssid = "my-network" aaa = { config = routeros_wifi_aaa.aaa1.name } channel = { config = routeros_wifi_channel.channel1.name } datapath = { config = routeros_wifi_datapath.datapath1.name } security = { config = routeros_wifi_security.security1.name } } ================================================ FILE: examples/resources/routeros_wifi_datapath/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wifi/datapath get [print show-ids]] terraform import routeros_wifi_datapath.datapath1 '*1' ================================================ FILE: examples/resources/routeros_wifi_datapath/resource.tf ================================================ resource "routeros_wifi_datapath" "datapath1" { name = "datapath1" bridge = "bridge1" client_isolation = false } ================================================ FILE: examples/resources/routeros_wifi_interworking/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wifi/interworking get [print show-ids]] terraform import routeros_wifi_interworking.interworking1 '*1' #Or you can import a resource using one of its attributes terraform import routeros_wifi_interworking.interworking1 "name=xxx" ================================================ FILE: examples/resources/routeros_wifi_interworking/resource.tf ================================================ resource "routeros_wifi_interworking" "interworking1" { name = "interworking1" internet = true } ================================================ FILE: examples/resources/routeros_wifi_provisioning/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wifi/provisioning get [print show-ids]] terraform import routeros_wifi_provisioning.provisioning1 '*1' #Or you can import a resource using one of its attributes terraform import routeros_wifi_provisioning.provisioning1 "name=xxx" ================================================ FILE: examples/resources/routeros_wifi_provisioning/resource.tf ================================================ resource "routeros_wifi_configuration" "configuration1" { country = "Netherlands" manager = "capsman" mode = "ap" name = "configuration1" ssid = "my-network" } resource "routeros_wifi_provisioning" "provisioning1" { action = "create-enabled" master_configuration = routeros_wifi_configuration.configuration1.name name_format = "cap1:" radio_mac = "00:11:22:33:44:55" } ================================================ FILE: examples/resources/routeros_wifi_security/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wifi/security get [print show-ids]] terraform import routeros_wifi_security.security1 '*1' #Or you can import a resource using one of its attributes terraform import routeros_wifi_security.security1 "name=xxx" ================================================ FILE: examples/resources/routeros_wifi_security/resource.tf ================================================ resource "routeros_wifi_security" "security1" { name = "security1" authentication_types = ["wpa2-psk", "wpa3-psk"] ft = true ft_preserve_vlanid = true passphrase = "password" } ================================================ FILE: examples/resources/routeros_wifi_security_multi_passphrase/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/wifi/security/multi/passphrase get [print show-ids]] terraform import routeros_wifi_security_multi_passphrase.test *3 #Or you can import a resource using one of its attributes terraform import routeros_wifi_security_multi_passphrase.test "comment=xxx" ================================================ FILE: examples/resources/routeros_wifi_security_multi_passphrase/resource.tf ================================================ resource "routeros_wifi_security_multi_passphrase" "test" { group = "gr-123" passphrase = data.vault_kv_secret_v2.wifi_security.data["test"] } ================================================ FILE: examples/resources/routeros_wifi_steering/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/interface/wifi/steering get [print show-ids]] terraform import routeros_wifi_steering.steering1 '*1' #Or you can import a resource using one of its attributes terraform import routeros_wifi_steering.steering1 "name=xxx" ================================================ FILE: examples/resources/routeros_wifi_steering/resource.tf ================================================ resource "routeros_wifi_steering" "steering1" { name = "steering1" neighbor_group = ["something"] } ================================================ FILE: examples/resources/routeros_wireguard_keys/resource.tf ================================================ resource "routeros_wireguard_keys" "wgk" { number = 3 } output "wg_keys" { value = routeros_wireguard_keys.wgk.keys[*] sensitive = true } output "wg_key" { value = nonsensitive(routeros_wireguard_keys.wgk.keys[1].public) } ================================================ FILE: examples/resources/routeros_zerotier/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/zerotier get [print show-ids]] terraform import routeros_zerotier.zt1 '*1' #Or you can import a resource using one of its attributes terraform import routeros_zerotier.zt1 "name=xxx" ================================================ FILE: examples/resources/routeros_zerotier/resource.tf ================================================ resource "zerotier_identity" "identity" {} resource "routeros_zerotier" "zt1" { comment = "ZeroTier Central" identity = zerotier_identity.identity.private_key interfaces = ["all"] name = "zt1" } ================================================ FILE: examples/resources/routeros_zerotier_controller/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/zerotier/controller get [print show-ids]] terraform import routeros_zerotier_controller.test '*1' #Or you can import a resource using one of its attributes terraform import routeros_zerotier_controller.test "name=xxx" ================================================ FILE: examples/resources/routeros_zerotier_controller/resource.tf ================================================ resource "zerotier_identity" "identity" {} resource "routeros_zerotier" "zt1" { comment = "ZeroTier Central" identity = zerotier_identity.identity.private_key interfaces = ["all"] name = "zt1" } resource "routeros_zerotier_controller" "test" { instance = routeros_zerotier.zt1.name name = "test" network = "1234567812345678" private = true } ================================================ FILE: examples/resources/routeros_zerotier_interface/import.sh ================================================ #The ID can be found via API or the terminal #The command for the terminal is -> :put [/zerotier/interface get [print show-ids]] terraform import routeros_zerotier_interface.zerotier1 '*1' #Or you can import a resource using one of its attributes terraform import routeros_zerotier_interface.zerotier1 "name=xxx" ================================================ FILE: examples/resources/routeros_zerotier_interface/resource.tf ================================================ resource "zerotier_identity" "identity" {} resource "zerotier_network" "network" { name = "test" } resource "zerotier_member" "member" { authorized = true member_id = zerotier_identity.identity.id name = "test" network_id = zerotier_network.network.id hidden = false allow_ethernet_bridging = true no_auto_assign_ips = true } resource "routeros_zerotier" "zt1" { comment = "ZeroTier Central" identity = zerotier_identity.identity.private_key interfaces = ["all"] name = "zt1" } resource "routeros_zerotier_interface" "zerotier1" { allow_default = false allow_global = false allow_managed = false instance = routeros_zerotier.zt1.name name = "zerotier1" network = zerotier_network.network.id } ================================================ FILE: go.mod ================================================ module github.com/terraform-routeros/terraform-provider-routeros go 1.25.0 require ( github.com/fatih/color v1.18.0 github.com/go-routeros/routeros/v3 v3.0.1 github.com/hashicorp/go-cty v1.5.0 github.com/hashicorp/terraform-plugin-docs v0.24.0 github.com/hashicorp/terraform-plugin-log v0.10.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.40.0 github.com/sirupsen/logrus v1.9.4 github.com/sourcegraph/go-diff-patch v0.0.0-20240223163233-798fd1e94a8e ) require ( github.com/BurntSushi/toml v1.2.1 // indirect github.com/Kunde21/markdownfmt/v3 v3.1.0 // indirect github.com/ProtonMail/go-crypto v1.3.0 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/bmatcuk/doublestar/v4 v4.9.1 // indirect github.com/cloudflare/circl v1.6.3 // indirect github.com/hashicorp/cli v1.1.7 // indirect github.com/hashicorp/go-retryablehttp v0.7.8 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect github.com/sergi/go-diff v1.4.0 // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/yuin/goldmark v1.7.7 // indirect github.com/yuin/goldmark-meta v1.1.0 // indirect go.abhg.dev/goldmark/frontmatter v0.2.0 // indirect golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 // indirect golang.org/x/sync v0.20.0 // indirect golang.org/x/term v0.41.0 // indirect golang.org/x/tools v0.42.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect rsc.io/qr v0.2.0 // indirect ) require ( github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.2.0 // indirect github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/agext/levenshtein v1.2.3 // indirect github.com/armon/go-radix v1.0.0 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-checkpoint v0.5.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-plugin v1.7.0 // indirect github.com/hashicorp/go-uuid v1.0.3 github.com/hashicorp/go-version v1.8.0 // indirect github.com/hashicorp/hc-install v0.9.3 // indirect github.com/hashicorp/hcl/v2 v2.24.0 // indirect github.com/hashicorp/logutils v1.0.0 // indirect github.com/hashicorp/terraform-exec v0.25.0 // indirect github.com/hashicorp/terraform-json v0.27.2 // indirect github.com/hashicorp/terraform-plugin-go v0.31.0 // indirect github.com/hashicorp/terraform-plugin-testing v1.15.0 github.com/hashicorp/terraform-registry-address v0.4.0 // indirect github.com/hashicorp/terraform-svchost v0.1.1 // indirect github.com/hashicorp/yamux v0.1.2 // indirect github.com/huandu/xstrings v1.3.3 // indirect github.com/imdario/mergo v0.3.15 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mdp/qrterminal/v3 v3.2.1 github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/oklog/run v1.1.0 // indirect github.com/posener/complete v1.2.3 // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/zclconf/go-cty v1.17.0 // indirect golang.org/x/crypto v0.49.0 golang.org/x/mod v0.33.0 // indirect golang.org/x/net v0.51.0 // indirect golang.org/x/sys v0.42.0 // indirect golang.org/x/text v0.35.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/grpc v1.79.2 // indirect google.golang.org/protobuf v1.36.11 // indirect ) ================================================ FILE: go.sum ================================================ dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/Kunde21/markdownfmt/v3 v3.1.0 h1:KiZu9LKs+wFFBQKhrZJrFZwtLnCCWJahL+S+E/3VnM0= github.com/Kunde21/markdownfmt/v3 v3.1.0/go.mod h1:tPXN1RTyOzJwhfHoon9wUr4HGYmWgVxSQN6VBJDkrVc= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bmatcuk/doublestar/v4 v4.9.1 h1:X8jg9rRZmJd4yRy7ZeNDRnM+T3ZfHv15JiBJ/avrEXE= github.com/bmatcuk/doublestar/v4 v4.9.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= github.com/go-git/go-git/v5 v5.16.5 h1:mdkuqblwr57kVfXri5TTH+nMFLNUxIj9Z7F5ykFbw5s= github.com/go-git/go-git/v5 v5.16.5/go.mod h1:QOMLpNf1qxuSY4StA/ArOdfFR2TrKEjJiye2kel2m+M= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-routeros/routeros/v3 v3.0.1 h1:FdNKlF6Hst8nkHr0dIvD54pQ+dZ8sHOJfQSVRKz0BFg= github.com/go-routeros/routeros/v3 v3.0.1/go.mod h1:j4mq65czXfKtHsdLkgVv8w7sNzyhLZy1TKi2zQDMpiQ= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/cli v1.1.7 h1:/fZJ+hNdwfTSfsxMBa9WWMlfjUZbX8/LnUxgAd7lCVU= github.com/hashicorp/cli v1.1.7/go.mod h1:e6Mfpga9OCT1vqzFuoGZiiF/KaG9CbUfO5s3ghU3YgU= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU= github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-cty v1.5.0 h1:EkQ/v+dDNUqnuVpmS5fPqyY71NXVgT5gf32+57xY8g0= github.com/hashicorp/go-cty v1.5.0/go.mod h1:lFUCG5kd8exDobgSfyj4ONE/dc822kiYMguVKdHGMLM= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-plugin v1.7.0 h1:YghfQH/0QmPNc/AZMTFE3ac8fipZyZECHdDPshfk+mA= github.com/hashicorp/go-plugin v1.7.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8= github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/hc-install v0.9.3 h1:1H4dgmgzxEVwT6E/d/vIL5ORGVKz9twRwDw+qA5Hyho= github.com/hashicorp/hc-install v0.9.3/go.mod h1:FQlQ5I3I/X409N/J1U4pPeQQz1R3BoV0IysB7aiaQE0= github.com/hashicorp/hcl/v2 v2.24.0 h1:2QJdZ454DSsYGoaE6QheQZjtKZSUs9Nh2izTWiwQxvE= github.com/hashicorp/hcl/v2 v2.24.0/go.mod h1:oGoO1FIQYfn/AgyOhlg9qLC6/nOJPX3qGbkZpYAcqfM= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/terraform-exec v0.25.0 h1:Bkt6m3VkJqYh+laFMrWIpy9KHYFITpOyzRMNI35rNaY= github.com/hashicorp/terraform-exec v0.25.0/go.mod h1:dl9IwsCfklDU6I4wq9/StFDp7dNbH/h5AnfS1RmiUl8= github.com/hashicorp/terraform-json v0.27.2 h1:BwGuzM6iUPqf9JYM/Z4AF1OJ5VVJEEzoKST/tRDBJKU= github.com/hashicorp/terraform-json v0.27.2/go.mod h1:GzPLJ1PLdUG5xL6xn1OXWIjteQRT2CNT9o/6A9mi9hE= github.com/hashicorp/terraform-plugin-docs v0.24.0 h1:YNZYd+8cpYclQyXbl1EEngbld8w7/LPOm99GD5nikIU= github.com/hashicorp/terraform-plugin-docs v0.24.0/go.mod h1:YLg+7LEwVmRuJc0EuCw0SPLxuQXw5mW8iJ5ml/kvi+o= github.com/hashicorp/terraform-plugin-go v0.31.0 h1:0Fz2r9DQ+kNNl6bx8HRxFd1TfMKUvnrOtvJPmp3Z0q8= github.com/hashicorp/terraform-plugin-go v0.31.0/go.mod h1:A88bDhd/cW7FnwqxQRz3slT+QY6yzbHKc6AOTtmdeS8= github.com/hashicorp/terraform-plugin-log v0.10.0 h1:eu2kW6/QBVdN4P3Ju2WiB2W3ObjkAsyfBsL3Wh1fj3g= github.com/hashicorp/terraform-plugin-log v0.10.0/go.mod h1:/9RR5Cv2aAbrqcTSdNmY1NRHP4E3ekrXRGjqORpXyB0= github.com/hashicorp/terraform-plugin-sdk/v2 v2.40.0 h1:MKS/2URqeJRwJdbOfcbdsZCq/IRrNkqJNN0GtVIsuGs= github.com/hashicorp/terraform-plugin-sdk/v2 v2.40.0/go.mod h1:PuG4P97Ju3QXW6c6vRkRadWJbvnEu2Xh+oOuqcYOqX4= github.com/hashicorp/terraform-plugin-testing v1.15.0 h1:/fimKyl0YgD7aAtJkuuAZjwBASXhCIwWqMbDLnKLMe4= github.com/hashicorp/terraform-plugin-testing v1.15.0/go.mod h1:bGXMw7bE95EiZhSBV3rM2W8TiffaPTDuLS+HFI/lIYs= github.com/hashicorp/terraform-registry-address v0.4.0 h1:S1yCGomj30Sao4l5BMPjTGZmCNzuv7/GDTDX99E9gTk= github.com/hashicorp/terraform-registry-address v0.4.0/go.mod h1:LRS1Ay0+mAiRkUyltGT+UHWkIqTFvigGn/LbMshfflE= github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ= github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc= github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94= github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mdp/qrterminal/v3 v3.2.1 h1:6+yQjiiOsSuXT5n9/m60E54vdgFsw0zhADHhHLrFet4= github.com/mdp/qrterminal/v3 v3.2.1/go.mod h1:jOTmXvnBsMy5xqLniO0R++Jmjs2sTm9dFSuQ5kpz/SU= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= github.com/sourcegraph/go-diff-patch v0.0.0-20240223163233-798fd1e94a8e h1:H+jDTUeF+SVd4ApwnSFoew8ZwGNRfgb9EsZc7LcocAg= github.com/sourcegraph/go-diff-patch v0.0.0-20240223163233-798fd1e94a8e/go.mod h1:VsUklG6OQo7Ctunu0gS3AtEOCEc2kMB6r5rKzxAes58= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.7.7 h1:5m9rrB1sW3JUMToKFQfb+FGt1U7r57IHu5GrYrG2nqU= github.com/yuin/goldmark v1.7.7/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc= github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0= github.com/zclconf/go-cty v1.17.0 h1:seZvECve6XX4tmnvRzWtJNHdscMtYEx5R7bnnVyd/d0= github.com/zclconf/go-cty v1.17.0/go.mod h1:wqFzcImaLTI6A5HfsRwB0nj5n0MRZFwmey8YoFPPs3U= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= go.abhg.dev/goldmark/frontmatter v0.2.0 h1:P8kPG0YkL12+aYk2yU3xHv4tcXzeVnN+gU0tJ5JnxRw= go.abhg.dev/goldmark/frontmatter v0.2.0/go.mod h1:XqrEkZuM57djk7zrlRUB02x8I5J0px76YjkOzhB4YlU= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 h1:EDuYyU/MkFXllv9QF9819VlI9a4tzGuCbhG0ExK9o1U= golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww= google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= google.golang.org/grpc v1.79.2 h1:fRMD94s2tITpyJGtBBn7MkMseNpOZU8ZxgC3MMBaXRU= google.golang.org/grpc v1.79.2/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY= rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs= ================================================ FILE: main.go ================================================ package main import ( "flag" "github.com/hashicorp/terraform-plugin-sdk/v2/plugin" "github.com/terraform-routeros/terraform-provider-routeros/routeros" ) // Generate the Terraform provider documentation using `tfplugindocs`: //go:generate go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs func main() { var debug bool // https://github.com/hashicorp/terraform-docs-common/blob/main/website/docs/plugin/debugging.mdx // https://developer.hashicorp.com/terraform/plugin/debugging flag.BoolVar(&debug, "debug", false, "set to true to run the provider with support for debuggers like delve") flag.Parse() plugin.Serve(&plugin.ServeOpts{ ProviderAddr: "terraform-routeros/routeros", ProviderFunc: routeros.NewProvider, Debug: debug, }) } ================================================ FILE: package.json ================================================ { "name": "terraform-provider-routeros", "version": "1.99.1", "repository": { "type": "git", "url": "https://github.com/terraform-routeros/terraform-provider-routeros" }, "dependencies": { "@semantic-release/changelog": "^6.0.2", "@semantic-release/commit-analyzer": "^9.0.2", "@semantic-release/exec": "^6.0.3", "@semantic-release/git": "^10.0.1", "@semantic-release/github": "^8.0.7", "conventional-changelog-conventionalcommits": ">= 8.0.0" }, "devDependencies": { "semantic-release": "^19.0.5" } } ================================================ FILE: routeros/datasource_files.go ================================================ package routeros import ( "context" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func DatasourceFiles() *schema.Resource { return &schema.Resource{ ReadContext: datasourceFilesRead, Schema: map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/file"), MetaId: PropId(Id), KeyFilter: PropFilterRw, "files": { Type: schema.TypeList, Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "id": { Type: schema.TypeString, Computed: true, }, "contents": { Type: schema.TypeString, Computed: true, }, "creation_time": { Type: schema.TypeString, Computed: true, }, "last_modified": { Type: schema.TypeString, Computed: true, }, "name": { Type: schema.TypeString, Computed: true, }, "package_architecture": { Type: schema.TypeString, Computed: true, }, "package_built_time": { Type: schema.TypeString, Computed: true, }, "package_name": { Type: schema.TypeString, Computed: true, }, "package_version": { Type: schema.TypeString, Computed: true, }, "size": { Type: schema.TypeInt, Computed: true, }, "type": { Type: schema.TypeString, Computed: true, }, }, }, }, }, } } func datasourceFilesRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { s := DatasourceFiles().Schema path := s[MetaResourcePath].Default.(string) res, err := ReadItemsFiltered(buildReadFilter(d.Get(KeyFilter).(map[string]interface{})), path, m.(Client)) if err != nil { return diag.FromErr(err) } return MikrotikResourceDataToTerraformDatasource(res, "files", s, d) } ================================================ FILE: routeros/datasource_files_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testDatasourceFiles = "data.routeros_files.files" func TestAccDatasourceFilesTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccDatasourceFilesConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testDatasourceFiles), ), }, }, }) }) } } func testAccDatasourceFilesConfig() string { return providerConfig + ` data "routeros_files" "files" {} ` } ================================================ FILE: routeros/datasource_interface_bridge_filter.go ================================================ package routeros // Script generated from sampled device MikroTik 7.11.2 (stable) on CHR AMD-x86_64 import ( "context" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func DatasourceInterfaceBridgeFilter() *schema.Resource { return &schema.Resource{ ReadContext: datasourceInterfaceBridgeFiltersRead, Schema: map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/bridge/filter"), MetaId: PropId(Id), KeyFilter: PropFilterRw, "filters": { Type: schema.TypeList, Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "id": { // Sample = .id: "*1" Type: schema.TypeString, Computed: true, }, "action": { // Sample = action: "drop" Type: schema.TypeString, Computed: true, }, "bytes": { // Sample = bytes: "0" Type: schema.TypeInt, Computed: true, }, "in_interface": { // Sample = chain: "ether1" Type: schema.TypeString, Computed: true, }, "chain": { // Sample = chain: "forward" Type: schema.TypeString, Computed: true, }, "comment": { // Sample = comment: "Drop data between cast ports" Type: schema.TypeString, Computed: true, }, "dynamic": { // Sample = dynamic: "false" Type: schema.TypeBool, Computed: true, }, "invalid": { // Sample = invalid: "false" Type: schema.TypeBool, Computed: true, }, "packets": { // Sample = packets: "0" Type: schema.TypeInt, Computed: true, }, "mac_protocol": { // Sample = mac_protocol: "0x890D" Type: schema.TypeString, Computed: true, }, }, }, }, }, } } func datasourceInterfaceBridgeFiltersRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { s := DatasourceInterfaceBridgeFilter().Schema path := s[MetaResourcePath].Default.(string) res, err := ReadItemsFiltered(buildReadFilter(d.Get(KeyFilter).(map[string]interface{})), path, m.(Client)) if err != nil { return diag.FromErr(err) } return MikrotikResourceDataToTerraformDatasource(res, "filters", s, d) } ================================================ FILE: routeros/datasource_interface_bridge_filter_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testDatasourceInterfaceBridgeFilter = "data.routeros_interface_bridge_filter.rules" func TestAccDatasourceInterfaceBridgeFilterTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccDatasourceInterfaceBridgeFilterConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testDatasourceInterfaceBridgeFilter), ), }, }, }) }) } } func testAccDatasourceInterfaceBridgeFilterConfig() string { return providerConfig + ` data "routeros_interface_bridge_filter" "rules" {} ` } ================================================ FILE: routeros/datasource_interfaces.go ================================================ package routeros import ( "context" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func DatasourceInterfaces() *schema.Resource { return &schema.Resource{ ReadContext: datasourceInterfacesRead, Schema: map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields( "fp_rx_byte", "fp_rx_packet", "fp_tx_byte", "fp_tx_packet", "link_downs", "rx_byte", "rx_drop", "rx_error", "rx_packet", "tx_byte", "tx_drop", "tx_error", "tx_packet", "tx_queue_drop", ), KeyFilter: PropFilterRw, "interfaces": { Type: schema.TypeList, Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "id": { Type: schema.TypeString, Computed: true, }, "actual_mtu": { Type: schema.TypeInt, Computed: true, }, "default_name": { Type: schema.TypeString, Computed: true, }, "dynamic": { Type: schema.TypeBool, Computed: true, }, "comment": { Type: schema.TypeString, Computed: true, }, "disabled": { Type: schema.TypeBool, Computed: true, }, "inactive": { Type: schema.TypeBool, Computed: true, }, "l2mtu": { Type: schema.TypeInt, Computed: true, }, "last_link_down_time": { Type: schema.TypeString, Computed: true, }, "last_link_up_time": { Type: schema.TypeString, Computed: true, }, "mac_address": { Type: schema.TypeString, Computed: true, }, "max_l2mtu": { Type: schema.TypeInt, Computed: true, }, // Can be - 'auto' "mtu": { Type: schema.TypeString, Computed: true, }, "name": { Type: schema.TypeString, Computed: true, }, "running": { Type: schema.TypeBool, Computed: true, }, "slave": { Type: schema.TypeBool, Computed: true, }, "type": { Type: schema.TypeString, Computed: true, }, }, }, }, }, } } func datasourceInterfacesRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { s := DatasourceInterfaces().Schema path := s[MetaResourcePath].Default.(string) res, err := ReadItemsFiltered(buildReadFilter(d.Get(KeyFilter).(map[string]interface{})), path, m.(Client)) if err != nil { return diag.FromErr(err) } return MikrotikResourceDataToTerraformDatasource(res, "interfaces", s, d) } ================================================ FILE: routeros/datasource_interfaces_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testDatasourceInterfaces = "data.routeros_interfaces.interfaces" func TestAccDatasourceInterfacesTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccDatasourceInterfacesConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testDatasourceInterfaces), ), }, }, }) }) } } func testAccDatasourceInterfacesConfig() string { return providerConfig + ` data "routeros_interfaces" "interfaces" {} ` } ================================================ FILE: routeros/datasource_ip_address_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testDatasourceIpAddressesAddress = "data.routeros_ip_addresses.addresses" func TestAccDatasourceIpAddressesTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccDatasourceIpAddressesConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testDatasourceIpAddressesAddress), ), }, }, }) }) } } func testAccDatasourceIpAddressesConfig() string { return providerConfig + ` data "routeros_ip_addresses" "addresses" {} ` } ================================================ FILE: routeros/datasource_ip_addresses.go ================================================ package routeros import ( "context" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func DatasourceIPAddresses() *schema.Resource { return &schema.Resource{ ReadContext: datasourceIPAddressesRead, Schema: map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/address"), MetaId: PropId(Id), KeyFilter: PropFilterRw, "addresses": { Type: schema.TypeList, Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "id": { Type: schema.TypeString, Computed: true, }, "actual_interface": { Type: schema.TypeString, Computed: true, }, "address": { Type: schema.TypeString, Computed: true, }, KeyComment: { Type: schema.TypeString, Computed: true, }, "disabled": { Type: schema.TypeBool, Computed: true, }, "dynamic": { Type: schema.TypeBool, Computed: true, }, KeyInterface: PropInterfaceRw, "invalid": { Type: schema.TypeBool, Computed: true, }, "network": { Type: schema.TypeString, Computed: true, }, "slave": { Type: schema.TypeBool, Computed: true, }, }, }, }, }, } } func datasourceIPAddressesRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { s := DatasourceIPAddresses().Schema path := s[MetaResourcePath].Default.(string) res, err := ReadItemsFiltered(buildReadFilter(d.Get(KeyFilter).(map[string]interface{})), path, m.(Client)) if err != nil { return diag.FromErr(err) } return MikrotikResourceDataToTerraformDatasource(res, "addresses", s, d) } ================================================ FILE: routeros/datasource_ip_arp.go ================================================ package routeros // Script generated from sampled device MikroTik 7.11.2 (stable) on CHR AMD-x86_64 import ( "context" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func DatasourceIpArp() *schema.Resource { return &schema.Resource{ ReadContext: datasourceIpArpRead, Schema: map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/arp"), MetaId: PropId(Id), KeyFilter: PropFilterRw, "data": { Type: schema.TypeList, Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "id": { // Sample = .id: "*1" Type: schema.TypeString, Computed: true, }, "dhcp": { // Sample = DHCP: "false" Type: schema.TypeBool, Computed: true, }, "address": { // Sample = address: "192.168.9.10" Type: schema.TypeString, Computed: true, }, "complete": { // Sample = complete: "true" Type: schema.TypeBool, Computed: true, }, "disabled": { // Sample = disabled: "false" Type: schema.TypeBool, Computed: true, }, "dynamic": { // Sample = dynamic: "true" Type: schema.TypeBool, Computed: true, }, "interface": { // Sample = interface: "ether1" Type: schema.TypeString, Computed: true, }, "invalid": { // Sample = invalid: "false" Type: schema.TypeBool, Computed: true, }, "mac_address": { // Sample = mac-address: "70:85:C2:37:5A:21" Type: schema.TypeString, Computed: true, }, "published": { // Sample = published: "false" Type: schema.TypeBool, Computed: true, }, }, }, }, }, } } func datasourceIpArpRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { s := DatasourceIpArp().Schema path := s[MetaResourcePath].Default.(string) res, err := ReadItemsFiltered(buildReadFilter(d.Get(KeyFilter).(map[string]interface{})), path, m.(Client)) if err != nil { return diag.FromErr(err) } return MikrotikResourceDataToTerraformDatasource(res, "data", s, d) } ================================================ FILE: routeros/datasource_ip_arp_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testDatasourceIpArp = "data.routeros_ip_arp.data" func TestAccDatasourceIpArpTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccDatasourceIpArpConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testDatasourceIpArp), ), }, }, }) }) } } func testAccDatasourceIpArpConfig() string { return providerConfig + ` data "routeros_ip_arp" "data" {} ` } ================================================ FILE: routeros/datasource_ip_dhcp_server_leases.go ================================================ package routeros // Script generated from sampled device MikroTik 7.11.2 (stable) on CHR AMD-x86_64 import ( "context" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func DatasourceIpDhcpServerLeases() *schema.Resource { return &schema.Resource{ ReadContext: datasourceIpDhcpServerLeasesRead, Schema: map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/dhcp-server/lease"), MetaId: PropId(Id), KeyFilter: PropFilterRw, "data": { Type: schema.TypeList, Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "id": { // Sample = .id: "*1" Type: schema.TypeString, Computed: true, }, "active_client_id": { Type: schema.TypeString, Computed: true, }, "active_server": { Type: schema.TypeString, Computed: true, }, "active_address": { Type: schema.TypeString, Computed: true, }, "active_mac_address": { Type: schema.TypeString, Computed: true, }, "address": { // Sample = address: "192.168.0.1" Type: schema.TypeString, Computed: true, }, "address_lists": { // Sample = address-lists: "" Type: schema.TypeString, Computed: true, }, "age": { Type: schema.TypeString, Computed: true, }, "allow_dual_stack_queue": { Type: schema.TypeBool, Computed: true, }, "blocked": { // Sample = blocked: "false" Type: schema.TypeBool, Computed: true, }, "comment": { // Sample = comment: "server1 " Type: schema.TypeString, Computed: true, }, "class_id": { Type: schema.TypeString, Computed: true, }, "client_id": { Type: schema.TypeString, Computed: true, }, "dhcp_option": { // Sample = dhcp-option: "" Type: schema.TypeString, Computed: true, }, "disabled": { // Sample = disabled: "true" Type: schema.TypeBool, Computed: true, }, "dynamic": { // Sample = dynamic: "false" Type: schema.TypeBool, Computed: true, }, "expires_after": { Type: schema.TypeString, Computed: true, }, "host_name": { Type: schema.TypeString, Computed: true, }, "last_seen": { // Sample = last-seen: "never" Type: schema.TypeString, Computed: true, }, "mac_address": { // Sample = mac-address: "00:0C:29:00:01:A0" Type: schema.TypeString, Computed: true, }, "radius": { // Sample = radius: "false" Type: schema.TypeBool, Computed: true, }, "server": { // Sample = server: "bridge_dhcp_lan" Type: schema.TypeString, Computed: true, }, "status": { // Sample = status: "waiting" Type: schema.TypeString, Computed: true, }, }, }, }, }, } } func datasourceIpDhcpServerLeasesRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { s := DatasourceIpDhcpServerLeases().Schema path := s[MetaResourcePath].Default.(string) res, err := ReadItemsFiltered(buildReadFilter(d.Get(KeyFilter).(map[string]interface{})), path, m.(Client)) if err != nil { return diag.FromErr(err) } return MikrotikResourceDataToTerraformDatasource(res, "data", s, d) } ================================================ FILE: routeros/datasource_ip_firewall.go ================================================ package routeros import ( "context" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) var ipFirewallSections = []string{"address_list", "nat", "mangle", "rules"} func DatasourceIPFirewall() *schema.Resource { return &schema.Resource{ ReadContext: datasourceIPFirewallFilterRead, Description: `This datasource contains all supported firewall resources: - address_list - nat - mangle - rules (aka filter) `, Schema: map[string]*schema.Schema{ MetaSkipFields: PropSkipFields("packets"), "address_list": getIPFirewallAddrListSchema(), "mangle": getIPFirewallMangleSchema(), "nat": getIPFirewallNatSchema(), "rules": getIPFirewallFilterSchema(), }, } } func datasourceIPFirewallFilterRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { var diags diag.Diagnostics basePath := "/ip/firewall/" s := DatasourceIPFirewall().Schema var isEmpty = true for _, section := range ipFirewallSections { isEmpty = isEmpty && len(d.Get(section).([]interface{})) == 0 } if isEmpty { return diag.Diagnostics{ diag.Diagnostic{ Severity: diag.Error, Summary: "You must specify at least one return section of the resource.", Detail: "Please specify one or more sections of the firewall, information from which will be " + "returned as a result of the data source query: rules{}, nat { filter = {...}}, etc.", }, } } for _, section := range ipFirewallSections { if len(d.Get(section).([]interface{})) == 0 { continue } path := basePath // The filtering section is named 'rules' to avoid confusion: filter { filter = { ... }}. if section == "rules" { path += "filter" } else { // Kebab case! path += SnakeToKebab(section) } // To handle drift. s[MetaResourcePath] = PropResourcePath(path) // Snake case! var res []MikrotikItem for _, sectionResourceData := range d.Get(section).([]interface{}) { filter := sectionResourceData.(map[string]interface{})[KeyFilter].(map[string]interface{}) r, err := ReadItemsFiltered(buildReadFilter(filter), path, m.(Client)) if err != nil { return diag.FromErr(err) } res = append(res, *r...) } diags = append(diags, MikrotikResourceDataToTerraformDatasource(&res, section, s, d)...) } return diags } ================================================ FILE: routeros/datasource_ip_firewall_addr_list.go ================================================ package routeros import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" func getIPFirewallAddrListSchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeList, Computed: true, Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ KeyFilter: PropFilterRw, "id": { Type: schema.TypeString, Computed: true, }, "address": { Type: schema.TypeString, Computed: true, }, KeyComment: { Type: schema.TypeString, Computed: true, }, "creation_time": { Type: schema.TypeString, Computed: true, }, KeyDisabled: { Type: schema.TypeBool, Computed: true, }, KeyDynamic: { Type: schema.TypeBool, Computed: true, }, "list": { Type: schema.TypeString, Computed: true, }, "timeout": { Type: schema.TypeString, Computed: true, }, }, }, } } ================================================ FILE: routeros/datasource_ip_firewall_filter.go ================================================ package routeros import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" func getIPFirewallFilterSchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeList, Computed: true, Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ KeyFilter: PropFilterRw, "id": { Type: schema.TypeString, Computed: true, }, "action": { Type: schema.TypeString, Computed: true, }, "address_list": { Type: schema.TypeString, Computed: true, }, "address_list_timeout": { Type: schema.TypeString, Computed: true, }, "bytes": { Type: schema.TypeInt, Computed: true, }, "chain": { Type: schema.TypeString, Computed: true, }, KeyComment: { Type: schema.TypeString, Computed: true, }, "connection_bytes": { Type: schema.TypeString, Computed: true, }, "connection_limit": { Type: schema.TypeString, Computed: true, }, "connection_mark": { Type: schema.TypeString, Computed: true, }, "connection_nat_state": { Type: schema.TypeString, Computed: true, }, "connection_rate": { Type: schema.TypeString, Computed: true, }, "connection_state": { Type: schema.TypeString, Computed: true, }, "connection_type": { Type: schema.TypeString, Computed: true, }, "content": { Type: schema.TypeString, Computed: true, }, KeyDisabled: { Type: schema.TypeBool, Computed: true, }, "dscp": { Type: schema.TypeInt, Computed: true, }, "dst_address": { Type: schema.TypeString, Computed: true, }, "dst_address_list": { Type: schema.TypeString, Computed: true, }, "dst_address_type": { Type: schema.TypeString, Computed: true, }, "dst_limit": { Type: schema.TypeString, Computed: true, }, "dst_port": { Type: schema.TypeString, Computed: true, }, KeyDynamic: { Type: schema.TypeBool, Computed: true, }, "fragment": { Type: schema.TypeBool, Computed: true, }, "hotspot": { Type: schema.TypeString, Computed: true, }, "icmp_options": { Type: schema.TypeString, Computed: true, }, "in_bridge_port": { Type: schema.TypeString, Computed: true, }, "in_bridge_port_list": { Type: schema.TypeString, Computed: true, }, "in_interface": { Type: schema.TypeString, Computed: true, }, "in_interface_list": { Type: schema.TypeString, Computed: true, }, "ingress_priority": { Type: schema.TypeInt, Computed: true, }, "invalid": { Type: schema.TypeBool, Computed: true, }, "ipsec_policy": { Type: schema.TypeString, Computed: true, }, "ipv4_options": { Type: schema.TypeString, Computed: true, }, "jump_target": { Type: schema.TypeString, Computed: true, }, "hw_offload": { Type: schema.TypeBool, Computed: true, }, "layer7_protocol": { Type: schema.TypeString, Computed: true, }, "limit": { Type: schema.TypeString, Computed: true, }, "log": { Type: schema.TypeBool, Computed: true, }, "log_prefix": { Type: schema.TypeString, Computed: true, }, "nth": { Type: schema.TypeString, Computed: true, }, "out_bridge_port": { Type: schema.TypeString, Computed: true, }, "out_bridge_port_list": { Type: schema.TypeString, Computed: true, }, "out_interface": { Type: schema.TypeString, Computed: true, }, "out_interface_list": { Type: schema.TypeString, Computed: true, }, "packet_mark": { Type: schema.TypeString, Computed: true, }, "packet_size": { Type: schema.TypeString, Computed: true, }, "per_connection_classifier": { Type: schema.TypeString, Computed: true, }, "port": { Type: schema.TypeString, Computed: true, }, "priority": { Type: schema.TypeInt, Computed: true, }, "protocol": { Type: schema.TypeString, Computed: true, }, "psd": { Type: schema.TypeString, Computed: true, }, "random": { Type: schema.TypeInt, Computed: true, }, "reject_with": { Type: schema.TypeString, Computed: true, }, "routing_table": { Type: schema.TypeString, Computed: true, }, "routing_mark": { Type: schema.TypeString, Computed: true, }, "src_address": { Type: schema.TypeString, Computed: true, }, "src_address_list": { Type: schema.TypeString, Computed: true, }, "src_address_type": { Type: schema.TypeString, Computed: true, }, "src_port": { Type: schema.TypeString, Computed: true, }, "src_mac_address": { Type: schema.TypeString, Computed: true, }, "tcp_flags": { Type: schema.TypeString, Computed: true, }, "tcp_mss": { Type: schema.TypeString, Computed: true, }, "time": { Type: schema.TypeString, Computed: true, }, "tls_host": { Type: schema.TypeString, Computed: true, }, "ttl": { Type: schema.TypeString, Computed: true, }, }, }, } } ================================================ FILE: routeros/datasource_ip_firewall_mangle.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func getIPFirewallMangleSchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeList, Computed: true, Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ KeyFilter: PropFilterRw, "id": { Type: schema.TypeString, Computed: true, }, "action": { Type: schema.TypeString, Computed: true, }, "address_list": { Type: schema.TypeString, Computed: true, }, "address_list_timeout": { Type: schema.TypeString, Computed: true, }, "bytes": { Type: schema.TypeInt, Computed: true, }, "chain": { Type: schema.TypeString, Computed: true, }, KeyComment: { Type: schema.TypeString, Computed: true, }, "connection_bytes": { Type: schema.TypeString, Computed: true, }, "connection_limit": { Type: schema.TypeString, Computed: true, }, "connection_mark": { Type: schema.TypeString, Computed: true, }, "connection_nat_state": { Type: schema.TypeString, Computed: true, }, "connection_rate": { Type: schema.TypeString, Computed: true, }, "connection_state": { Type: schema.TypeString, Computed: true, }, "connection_type": { Type: schema.TypeString, Computed: true, }, "content": { Type: schema.TypeString, Computed: true, }, KeyDisabled: { Type: schema.TypeBool, Computed: true, }, "dscp": { Type: schema.TypeInt, Computed: true, }, "dst_address": { Type: schema.TypeString, Computed: true, }, "dst_address_list": { Type: schema.TypeString, Computed: true, }, "dst_address_type": { Type: schema.TypeString, Computed: true, }, "dst_limit": { Type: schema.TypeString, Computed: true, }, "dst_port": { Type: schema.TypeString, Computed: true, }, KeyDynamic: { Type: schema.TypeBool, Computed: true, }, "fragment": { Type: schema.TypeBool, Computed: true, }, "hotspot": { Type: schema.TypeString, Computed: true, }, "icmp_options": { Type: schema.TypeString, Computed: true, }, "in_bridge_port": { Type: schema.TypeString, Computed: true, }, "in_bridge_port_list": { Type: schema.TypeString, Computed: true, }, "in_interface": { Type: schema.TypeString, Computed: true, }, "in_interface_list": { Type: schema.TypeString, Computed: true, }, "ingress_priority": { Type: schema.TypeInt, Computed: true, }, "invalid": { Type: schema.TypeBool, Computed: true, }, "ipsec_policy": { Type: schema.TypeString, Computed: true, }, "ipv4_options": { Type: schema.TypeString, Computed: true, }, "jump_target": { Type: schema.TypeString, Computed: true, }, "layer7_protocol": { Type: schema.TypeString, Computed: true, }, "limit": { Type: schema.TypeString, Computed: true, }, "log": { Type: schema.TypeBool, Computed: true, }, "log_prefix": { Type: schema.TypeString, Computed: true, }, "new_connection_mark": { Type: schema.TypeString, Computed: true, }, "new_dscp": { Type: schema.TypeInt, Computed: true, }, "new_mss": { Type: schema.TypeString, Computed: true, }, "new_packet_mark": { Type: schema.TypeString, Computed: true, }, "new_priority": { Type: schema.TypeString, Computed: true, }, "new_routing_mark": { Type: schema.TypeString, Computed: true, }, "new_ttl": { Type: schema.TypeString, Computed: true, }, "nth": { Type: schema.TypeString, Computed: true, }, "out_bridge_port": { Type: schema.TypeString, Computed: true, }, "out_bridge_port_list": { Type: schema.TypeString, Computed: true, }, "out_interface": { Type: schema.TypeString, Computed: true, }, "out_interface_list": { Type: schema.TypeString, Computed: true, }, "packet_mark": { Type: schema.TypeString, Computed: true, }, "packet_size": { Type: schema.TypeString, Computed: true, }, "passthrough": { Type: schema.TypeBool, Computed: true, }, "per_connection_classifier": { Type: schema.TypeString, Computed: true, }, "port": { Type: schema.TypeString, Computed: true, }, "protocol": { Type: schema.TypeString, Computed: true, }, "psd": { Type: schema.TypeString, Computed: true, }, "random": { Type: schema.TypeInt, Computed: true, }, "routing_mark": { Type: schema.TypeString, Computed: true, }, "route_dst": { Type: schema.TypeString, Computed: true, }, "src_address": { Type: schema.TypeString, Computed: true, }, "src_address_list": { Type: schema.TypeString, Computed: true, }, "src_address_type": { Type: schema.TypeString, Computed: true, }, "src_port": { Type: schema.TypeString, Computed: true, }, "src_mac_address": { Type: schema.TypeString, Computed: true, }, "tcp_flags": { Type: schema.TypeString, Computed: true, }, "tcp_mss": { Type: schema.TypeString, Computed: true, }, "time": { Type: schema.TypeString, Computed: true, }, "tls_host": { Type: schema.TypeString, Computed: true, }, "ttl": { Type: schema.TypeString, Computed: true, }, }, }, } } ================================================ FILE: routeros/datasource_ip_firewall_nat.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func getIPFirewallNatSchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeList, Computed: true, Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ KeyFilter: PropFilterRw, "id": { Type: schema.TypeString, Computed: true, }, "action": { Type: schema.TypeString, Computed: true, }, "address_list": { Type: schema.TypeString, Computed: true, }, "address_list_timeout": { Type: schema.TypeString, Computed: true, }, "bytes": { Type: schema.TypeInt, Computed: true, }, "chain": { Type: schema.TypeString, Computed: true, }, KeyComment: { Type: schema.TypeString, Computed: true, }, "connection_bytes": { Type: schema.TypeString, Computed: true, }, "connection_limit": { Type: schema.TypeString, Computed: true, }, "connection_mark": { Type: schema.TypeString, Computed: true, }, "connection_rate": { Type: schema.TypeString, Computed: true, }, "connection_type": { Type: schema.TypeString, Computed: true, }, "content": { Type: schema.TypeString, Computed: true, }, KeyDisabled: { Type: schema.TypeBool, Computed: true, }, "dscp": { Type: schema.TypeInt, Computed: true, }, "dst_address": { Type: schema.TypeString, Computed: true, }, "dst_address_list": { Type: schema.TypeString, Computed: true, }, "dst_address_type": { Type: schema.TypeString, Computed: true, }, "dst_limit": { Type: schema.TypeString, Computed: true, }, "dst_port": { Type: schema.TypeString, Computed: true, }, KeyDynamic: { Type: schema.TypeBool, Computed: true, }, "fragment": { Type: schema.TypeBool, Computed: true, }, "hotspot": { Type: schema.TypeString, Computed: true, }, "icmp_options": { Type: schema.TypeString, Computed: true, }, "in_bridge_port": { Type: schema.TypeString, Computed: true, }, "in_bridge_port_list": { Type: schema.TypeString, Computed: true, }, "in_interface": { Type: schema.TypeString, Computed: true, }, "in_interface_list": { Type: schema.TypeString, Computed: true, }, "ingress_priority": { Type: schema.TypeInt, Computed: true, }, "invalid": { Type: schema.TypeBool, Computed: true, }, "ipsec_policy": { Type: schema.TypeString, Computed: true, }, "ipv4_options": { Type: schema.TypeString, Computed: true, }, "jump_target": { Type: schema.TypeString, Computed: true, }, "layer7_protocol": { Type: schema.TypeString, Computed: true, }, "limit": { Type: schema.TypeString, Computed: true, }, "log": { Type: schema.TypeBool, Computed: true, }, "log_prefix": { Type: schema.TypeString, Computed: true, }, "nth": { Type: schema.TypeString, Computed: true, }, "out_bridge_port": { Type: schema.TypeString, Computed: true, }, "out_bridge_port_list": { Type: schema.TypeString, Computed: true, }, "out_interface": { Type: schema.TypeString, Computed: true, }, "out_interface_list": { Type: schema.TypeString, Computed: true, }, "packet_mark": { Type: schema.TypeString, Computed: true, }, "packet_size": { Type: schema.TypeString, Computed: true, }, "per_connection_classifier": { Type: schema.TypeString, Computed: true, }, "port": { Type: schema.TypeString, Computed: true, }, "priority": { Type: schema.TypeInt, Computed: true, }, "protocol": { Type: schema.TypeString, Computed: true, }, "psd": { Type: schema.TypeString, Computed: true, }, "random": { Type: schema.TypeInt, Computed: true, }, "routing_mark": { Type: schema.TypeString, Computed: true, }, "same_not_by_dst": { Type: schema.TypeBool, Computed: true, }, "socks5_port": { Type: schema.TypeString, Computed: true, }, "socks5_server": { Type: schema.TypeString, Computed: true, }, "src_address": { Type: schema.TypeString, Computed: true, }, "src_address_list": { Type: schema.TypeString, Computed: true, }, "src_address_type": { Type: schema.TypeString, Computed: true, }, "src_port": { Type: schema.TypeString, Computed: true, }, "src_mac_address": { Type: schema.TypeString, Computed: true, }, "tcp_mss": { Type: schema.TypeString, Computed: true, }, "time": { Type: schema.TypeString, Computed: true, }, "to_addresses": { Type: schema.TypeString, Computed: true, }, "to_ports": { Type: schema.TypeString, Computed: true, }, "ttl": { Type: schema.TypeString, Computed: true, }, }, }, } } ================================================ FILE: routeros/datasource_ip_firewall_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testDatasourceIpFirewall = "data.routeros_ip_firewall.fw" func TestAccDatasourceIpFirewallTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccDatasourceIpFirewallConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testDatasourceIpFirewall), ), }, }, }) }) } } func testAccDatasourceIpFirewallConfig() string { return providerConfig + ` data "routeros_ip_firewall" "fw" { address_list {} mangle {} nat {} rules {} } ` } ================================================ FILE: routeros/datasource_ip_routes.go ================================================ package routeros import ( "context" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func DatasourceIPRoutes() *schema.Resource { return &schema.Resource{ ReadContext: datasourceIPRoutesRead, Schema: map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/route"), MetaId: PropId(Id), KeyFilter: PropFilterRw, "routes": { Type: schema.TypeList, Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "id": { Type: schema.TypeString, Computed: true, }, "active": { Type: schema.TypeBool, Computed: true, }, "blackhole": { Type: schema.TypeBool, Computed: true, }, KeyComment: { Type: schema.TypeString, Computed: true, }, "connect": { Type: schema.TypeBool, Computed: true, }, "dhcp": { Type: schema.TypeBool, Computed: true, }, "disabled": { Type: schema.TypeBool, Computed: true, }, "distance": { Type: schema.TypeInt, Computed: true, }, "dst_address": { Type: schema.TypeString, Computed: true, }, "dynamic": { Type: schema.TypeBool, Computed: true, }, "ecmp": { Type: schema.TypeBool, Computed: true, }, "gateway": { Type: schema.TypeString, Computed: true, }, "hw_offloaded": { Type: schema.TypeBool, Computed: true, }, "immediate_gw": { Type: schema.TypeString, Computed: true, }, "inactive": { Type: schema.TypeBool, Computed: true, }, "local_address": { Type: schema.TypeString, Computed: true, }, "pref_src": { Type: schema.TypeString, Computed: true, }, "routing_table": { Type: schema.TypeString, Computed: true, }, "scope": { Type: schema.TypeInt, Computed: true, }, "static": { Type: schema.TypeBool, Computed: true, }, "suppress_hw_offload": { Type: schema.TypeBool, Computed: true, }, "target_scope": { Type: schema.TypeInt, Computed: true, }, "vrf_interface": { Type: schema.TypeString, Computed: true, }, }, }, }, }, } } func datasourceIPRoutesRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { s := DatasourceIPRoutes().Schema path := s[MetaResourcePath].Default.(string) res, err := ReadItemsFiltered(buildReadFilter(d.Get(KeyFilter).(map[string]interface{})), path, m.(Client)) if err != nil { return diag.FromErr(err) } return MikrotikResourceDataToTerraformDatasource(res, "routes", s, d) } ================================================ FILE: routeros/datasource_ip_routes_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testDatasourceIpRoutes = "data.routeros_ip_routes.routes" func TestAccDatasourceIpRoutesTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccDatasourceIpRoutesConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testDatasourceIpRoutes), ), }, }, }) }) } } func testAccDatasourceIpRoutesConfig() string { return providerConfig + ` data "routeros_ip_routes" "routes" {} ` } ================================================ FILE: routeros/datasource_ip_services.go ================================================ package routeros import ( "context" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func DatasourceIPServices() *schema.Resource { return &schema.Resource{ ReadContext: datasourceIPServicesRead, Schema: map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/service"), MetaId: PropId(Id), KeyFilter: PropFilterRw, "dynamic_services": { Type: schema.TypeList, Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "id": { Type: schema.TypeString, Computed: true, }, "address": { Type: schema.TypeString, Computed: true, }, "certificate": { Type: schema.TypeString, Computed: true, }, "connection": { Type: schema.TypeBool, Computed: true, }, "disabled": { Type: schema.TypeBool, Computed: true, }, "dynamic": { Type: schema.TypeBool, Computed: true, }, "invalid": { Type: schema.TypeString, Computed: true, }, "local": { Type: schema.TypeString, Computed: true, }, "max_sessions": { Type: schema.TypeInt, Computed: true, }, "name": { Type: schema.TypeString, Computed: true, }, "port": { Type: schema.TypeInt, Computed: true, }, "proto": { Type: schema.TypeString, Computed: true, }, "remote": { Type: schema.TypeString, Computed: true, }, "tls_version": { Type: schema.TypeString, Computed: true, }, "vrf": { Type: schema.TypeString, Computed: true, }, }, }, }, "services": { Type: schema.TypeList, Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "id": { Type: schema.TypeString, Computed: true, }, "address": { Type: schema.TypeString, Computed: true, }, "certificate": { Type: schema.TypeString, Computed: true, }, "connection": { Type: schema.TypeBool, Computed: true, }, "disabled": { Type: schema.TypeBool, Computed: true, }, "dynamic": { Type: schema.TypeBool, Computed: true, }, "invalid": { Type: schema.TypeString, Computed: true, }, "max_sessions": { Type: schema.TypeInt, Computed: true, }, "name": { Type: schema.TypeString, Computed: true, }, "port": { Type: schema.TypeInt, Computed: true, }, "proto": { Type: schema.TypeString, Computed: true, }, "tls_version": { Type: schema.TypeString, Computed: true, }, "vrf": { Type: schema.TypeString, Computed: true, }, }, }, }, }, } } func datasourceIPServicesRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { s := DatasourceIPServices().Schema path := s[MetaResourcePath].Default.(string) var filter = d.Get(KeyFilter).(map[string]interface{}) filter["dynamic"] = "false" var res, err = ReadItemsFiltered(buildReadFilter(filter), path, m.(Client)) if err != nil { return diag.FromErr(err) } var diags = MikrotikResourceDataToTerraformDatasource(res, "services", s, d) filter["dynamic"] = "true" res, err = ReadItemsFiltered(buildReadFilter(filter), path, m.(Client)) if err != nil { return diag.FromErr(err) } diags = append(diags, MikrotikResourceDataToTerraformDatasource(res, "dynamic_services", s, d)...) return diags } ================================================ FILE: routeros/datasource_ipv6_addresses.go ================================================ package routeros import ( "context" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func DatasourceIPv6Addresses() *schema.Resource { return &schema.Resource{ ReadContext: datasourceIPv6AddressesRead, Schema: map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ipv6/address"), MetaId: PropId(Id), KeyFilter: PropFilterRw, "addresses": { Type: schema.TypeList, Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "id": { Type: schema.TypeString, Computed: true, }, "actual_interface": { Type: schema.TypeString, Computed: true, }, "address": { Type: schema.TypeString, Computed: true, }, "advertise": { Type: schema.TypeBool, Computed: true, }, "deprecated": { Type: schema.TypeBool, Computed: true, }, "comment": { Type: schema.TypeString, Computed: true, }, "disabled": { Type: schema.TypeBool, Computed: true, }, "dynamic": { Type: schema.TypeBool, Computed: true, }, "eui_64": { Type: schema.TypeBool, Computed: true, }, "from_pool": { Type: schema.TypeString, Computed: true, }, "interface": { Type: schema.TypeString, Computed: true, }, "invalid": { Type: schema.TypeBool, Computed: true, }, "link_local": { Type: schema.TypeBool, Computed: true, }, "no_dad": { Type: schema.TypeBool, Computed: true, }, "slave": { Type: schema.TypeBool, Computed: true, }, }, }, }, }, } } func datasourceIPv6AddressesRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { s := DatasourceIPv6Addresses().Schema path := s[MetaResourcePath].Default.(string) res, err := ReadItemsFiltered(buildReadFilter(d.Get(KeyFilter).(map[string]interface{})), path, m.(Client)) if err != nil { return diag.FromErr(err) } return MikrotikResourceDataToTerraformDatasource(res, "addresses", s, d) } ================================================ FILE: routeros/datasource_ipv6_addresses_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testDatasourceIpv6AddressesAddress = "data.routeros_ipv6_addresses.addresses" func TestAccDatasourceIpv6AddressesTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccDatasourceIpv6AddressesConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testDatasourceIpv6AddressesAddress), ), }, }, }) }) } } func testAccDatasourceIpv6AddressesConfig() string { return providerConfig + ` data "routeros_ipv6_addresses" "addresses" {} ` } ================================================ FILE: routeros/datasource_ipv6_firewall.go ================================================ package routeros import ( "context" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) var ipv6firewallSections = []string{"rules"} func DatasourceIPv6Firewall() *schema.Resource { return &schema.Resource{ ReadContext: datasourceIPv6FirewallFilterRead, Description: `This datasource contains all supported firewall resources: - rules (aka filter) `, Schema: map[string]*schema.Schema{ MetaSkipFields: PropSkipFields("packets"), "mangle": getIPv6FirewallMangleSchema(), "nat": getIPv6FirewallNatSchema(), "rules": getIPv6FirewallFilterSchema(), }, } } func datasourceIPv6FirewallFilterRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { var diags diag.Diagnostics basePath := "/ipv6/firewall/" s := DatasourceIPv6Firewall().Schema var isEmpty = true for _, section := range ipv6firewallSections { isEmpty = isEmpty && len(d.Get(section).([]interface{})) == 0 } if isEmpty { return diag.Diagnostics{ diag.Diagnostic{ Severity: diag.Error, Summary: "You must specify at least one return section of the resource.", Detail: "Please specify one or more sections of the firewall, information from which will be " + "returned as a result of the data source query: rules{}, nat { filter = {...}}, etc.", }, } } for _, section := range ipv6firewallSections { if len(d.Get(section).([]interface{})) == 0 { continue } path := basePath // The filtering section is named 'rules' to avoid confusion: filter { filter = { ... }}. if section == "rules" { path += "filter" } else { // Kebab case! path += SnakeToKebab(section) } // To handle drift. s[MetaResourcePath] = PropResourcePath(path) // Snake case! var res []MikrotikItem for _, sectionResourceData := range d.Get(section).([]interface{}) { filter := sectionResourceData.(map[string]interface{})[KeyFilter].(map[string]interface{}) r, err := ReadItemsFiltered(buildReadFilter(filter), path, m.(Client)) if err != nil { return diag.FromErr(err) } res = append(res, *r...) } diags = append(diags, MikrotikResourceDataToTerraformDatasource(&res, section, s, d)...) } return diags } ================================================ FILE: routeros/datasource_ipv6_firewall_filter.go ================================================ package routeros import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" func getIPv6FirewallFilterSchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeList, Computed: true, Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ KeyFilter: PropFilterRw, "id": { Type: schema.TypeString, Computed: true, }, "action": { Type: schema.TypeString, Computed: true, }, "bytes": { Type: schema.TypeInt, Computed: true, }, "chain": { Type: schema.TypeString, Computed: true, }, KeyComment: { Type: schema.TypeString, Computed: true, }, "connection_bytes": { Type: schema.TypeString, Computed: true, }, "connection_limit": { Type: schema.TypeString, Computed: true, }, "connection_mark": { Type: schema.TypeString, Computed: true, }, "connection_nat_state": { Type: schema.TypeString, Computed: true, }, "connection_rate": { Type: schema.TypeString, Computed: true, }, "connection_state": { Type: schema.TypeString, Computed: true, }, "connection_type": { Type: schema.TypeString, Computed: true, }, "content": { Type: schema.TypeString, Computed: true, }, KeyDisabled: { Type: schema.TypeBool, Computed: true, }, "dscp": { Type: schema.TypeInt, Computed: true, }, "dst_address": { Type: schema.TypeString, Computed: true, }, "dst_address_list": { Type: schema.TypeString, Computed: true, }, "dst_address_type": { Type: schema.TypeString, Computed: true, }, "dst_limit": { Type: schema.TypeString, Computed: true, }, "dst_port": { Type: schema.TypeString, Computed: true, }, KeyDynamic: { Type: schema.TypeBool, Computed: true, }, "icmp_options": { Type: schema.TypeString, Computed: true, }, "in_bridge_port": { Type: schema.TypeString, Computed: true, }, "in_bridge_port_list": { Type: schema.TypeString, Computed: true, }, "in_interface": { Type: schema.TypeString, Computed: true, }, "in_interface_list": { Type: schema.TypeString, Computed: true, }, "ingress_priority": { Type: schema.TypeInt, Computed: true, }, "invalid": { Type: schema.TypeBool, Computed: true, }, "ipsec_policy": { Type: schema.TypeString, Computed: true, }, "limit": { Type: schema.TypeString, Computed: true, }, "log": { Type: schema.TypeBool, Computed: true, }, "log_prefix": { Type: schema.TypeString, Computed: true, }, "nth": { Type: schema.TypeString, Computed: true, }, "out_bridge_port": { Type: schema.TypeString, Computed: true, }, "out_bridge_port_list": { Type: schema.TypeString, Computed: true, }, "out_interface": { Type: schema.TypeString, Computed: true, }, "out_interface_list": { Type: schema.TypeString, Computed: true, }, "packet_mark": { Type: schema.TypeString, Computed: true, }, "packet_size": { Type: schema.TypeString, Computed: true, }, "per_connection_classifier": { Type: schema.TypeString, Computed: true, }, "port": { Type: schema.TypeString, Computed: true, }, "priority": { Type: schema.TypeInt, Computed: true, }, "protocol": { Type: schema.TypeString, Computed: true, }, "random": { Type: schema.TypeInt, Computed: true, }, "reject_with": { Type: schema.TypeString, Computed: true, }, "routing_table": { Type: schema.TypeString, Computed: true, }, "routing_mark": { Type: schema.TypeString, Computed: true, }, "src_address": { Type: schema.TypeString, Computed: true, }, "src_address_list": { Type: schema.TypeString, Computed: true, }, "src_address_type": { Type: schema.TypeString, Computed: true, }, "src_port": { Type: schema.TypeString, Computed: true, }, "src_mac_address": { Type: schema.TypeString, Computed: true, }, "tcp_flags": { Type: schema.TypeString, Computed: true, }, "tcp_mss": { Type: schema.TypeString, Computed: true, }, "time": { Type: schema.TypeString, Computed: true, }, "tls_host": { Type: schema.TypeString, Computed: true, }, "ttl": { Type: schema.TypeString, Computed: true, }, }, }, } } ================================================ FILE: routeros/datasource_ipv6_firewall_mangle.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func getIPv6FirewallMangleSchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeList, Computed: true, Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ KeyFilter: PropFilterRw, "id": { Type: schema.TypeString, Computed: true, }, "action": { Type: schema.TypeString, Computed: true, }, "address_list": { Type: schema.TypeString, Computed: true, }, "address_list_timeout": { Type: schema.TypeString, Computed: true, }, "bytes": { Type: schema.TypeInt, Computed: true, }, "chain": { Type: schema.TypeString, Computed: true, }, KeyComment: { Type: schema.TypeString, Computed: true, }, "connection_bytes": { Type: schema.TypeString, Computed: true, }, "connection_limit": { Type: schema.TypeString, Computed: true, }, "connection_mark": { Type: schema.TypeString, Computed: true, }, "connection_nat_state": { Type: schema.TypeString, Computed: true, }, "connection_rate": { Type: schema.TypeString, Computed: true, }, "connection_state": { Type: schema.TypeString, Computed: true, }, "connection_type": { Type: schema.TypeString, Computed: true, }, "content": { Type: schema.TypeString, Computed: true, }, KeyDisabled: { Type: schema.TypeBool, Computed: true, }, "dscp": { Type: schema.TypeInt, Computed: true, }, "dst_address": { Type: schema.TypeString, Computed: true, }, "dst_address_list": { Type: schema.TypeString, Computed: true, }, "dst_address_type": { Type: schema.TypeString, Computed: true, }, "dst_limit": { Type: schema.TypeString, Computed: true, }, "dst_port": { Type: schema.TypeString, Computed: true, }, KeyDynamic: { Type: schema.TypeBool, Computed: true, }, // no fragment, hotspot "icmp_options": { Type: schema.TypeString, Computed: true, }, "in_bridge_port": { Type: schema.TypeString, Computed: true, }, "in_bridge_port_list": { Type: schema.TypeString, Computed: true, }, "in_interface": { Type: schema.TypeString, Computed: true, }, "in_interface_list": { Type: schema.TypeString, Computed: true, }, "ingress_priority": { Type: schema.TypeInt, Computed: true, }, "invalid": { Type: schema.TypeBool, Computed: true, }, "ipsec_policy": { Type: schema.TypeString, Computed: true, }, "jump_target": { Type: schema.TypeString, Computed: true, }, // no jump target, layer7 "limit": { Type: schema.TypeString, Computed: true, }, "log": { Type: schema.TypeBool, Computed: true, }, "log_prefix": { Type: schema.TypeString, Computed: true, }, "new_connection_mark": { Type: schema.TypeString, Computed: true, }, "new_dscp": { Type: schema.TypeInt, Computed: true, }, "new_mss": { Type: schema.TypeString, Computed: true, }, "new_packet_mark": { Type: schema.TypeString, Computed: true, }, "new_priority": { Type: schema.TypeString, Computed: true, }, "new_routing_mark": { Type: schema.TypeString, Computed: true, }, "new_ttl": { Type: schema.TypeString, Computed: true, }, "nth": { Type: schema.TypeString, Computed: true, }, "out_bridge_port": { Type: schema.TypeString, Computed: true, }, "out_bridge_port_list": { Type: schema.TypeString, Computed: true, }, "out_interface": { Type: schema.TypeString, Computed: true, }, "out_interface_list": { Type: schema.TypeString, Computed: true, }, "packet_mark": { Type: schema.TypeString, Computed: true, }, "packet_size": { Type: schema.TypeString, Computed: true, }, "passthrough": { Type: schema.TypeBool, Computed: true, }, "per_connection_classifier": { Type: schema.TypeString, Computed: true, }, "port": { Type: schema.TypeString, Computed: true, }, "priority": { Type: schema.TypeInt, Computed: true, }, "protocol": { Type: schema.TypeString, Computed: true, }, "random": { Type: schema.TypeInt, Computed: true, }, "routing_mark": { Type: schema.TypeString, Computed: true, }, "src_address": { Type: schema.TypeString, Computed: true, }, "src_address_list": { Type: schema.TypeString, Computed: true, }, "src_address_type": { Type: schema.TypeString, Computed: true, }, "src_port": { Type: schema.TypeString, Computed: true, }, "src_mac_address": { Type: schema.TypeString, Computed: true, }, "tcp_flags": { Type: schema.TypeString, Computed: true, }, "tcp_mss": { Type: schema.TypeString, Computed: true, }, "time": { Type: schema.TypeString, Computed: true, }, "tls_host": { Type: schema.TypeString, Computed: true, }, "ttl": { Type: schema.TypeString, Computed: true, }, }, }, } } ================================================ FILE: routeros/datasource_ipv6_firewall_nat.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func getIPv6FirewallNatSchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeList, Computed: true, Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ KeyFilter: PropFilterRw, "id": { Type: schema.TypeString, Computed: true, }, "action": { Type: schema.TypeString, Computed: true, }, "address_list": { Type: schema.TypeString, Computed: true, }, "address_list_timeout": { Type: schema.TypeString, Computed: true, }, "bytes": { Type: schema.TypeInt, Computed: true, }, "chain": { Type: schema.TypeString, Computed: true, }, KeyComment: { Type: schema.TypeString, Computed: true, }, "connection_bytes": { Type: schema.TypeString, Computed: true, }, "connection_limit": { Type: schema.TypeString, Computed: true, }, "connection_mark": { Type: schema.TypeString, Computed: true, }, "connection_rate": { Type: schema.TypeString, Computed: true, }, "connection_type": { Type: schema.TypeString, Computed: true, }, "content": { Type: schema.TypeString, Computed: true, }, KeyDisabled: { Type: schema.TypeBool, Computed: true, }, "dscp": { Type: schema.TypeInt, Computed: true, }, "dst_address": { Type: schema.TypeString, Computed: true, }, "dst_address_list": { Type: schema.TypeString, Computed: true, }, "dst_address_type": { Type: schema.TypeString, Computed: true, }, "dst_limit": { Type: schema.TypeString, Computed: true, }, "dst_port": { Type: schema.TypeString, Computed: true, }, KeyDynamic: { Type: schema.TypeBool, Computed: true, }, "icmp_options": { Type: schema.TypeString, Computed: true, }, "in_bridge_port": { Type: schema.TypeString, Computed: true, }, "in_bridge_port_list": { Type: schema.TypeString, Computed: true, }, "in_interface": { Type: schema.TypeString, Computed: true, }, "in_interface_list": { Type: schema.TypeString, Computed: true, }, "ingress_priority": { Type: schema.TypeInt, Computed: true, }, "invalid": { Type: schema.TypeBool, Computed: true, }, "ipsec_policy": { Type: schema.TypeString, Computed: true, }, "jump_target": { Type: schema.TypeString, Computed: true, }, "limit": { Type: schema.TypeString, Computed: true, }, "log": { Type: schema.TypeBool, Computed: true, }, "log_prefix": { Type: schema.TypeString, Computed: true, }, "nth": { Type: schema.TypeString, Computed: true, }, "out_bridge_port": { Type: schema.TypeString, Computed: true, }, "out_bridge_port_list": { Type: schema.TypeString, Computed: true, }, "out_interface": { Type: schema.TypeString, Computed: true, }, "out_interface_list": { Type: schema.TypeString, Computed: true, }, "packet_mark": { Type: schema.TypeString, Computed: true, }, "packet_size": { Type: schema.TypeString, Computed: true, }, "per_connection_classifier": { Type: schema.TypeString, Computed: true, }, "port": { Type: schema.TypeString, Computed: true, }, "priority": { Type: schema.TypeInt, Computed: true, }, "protocol": { Type: schema.TypeString, Computed: true, }, "random": { Type: schema.TypeInt, Computed: true, }, "routing_mark": { Type: schema.TypeString, Computed: true, }, "src_address": { Type: schema.TypeString, Computed: true, }, "src_address_list": { Type: schema.TypeString, Computed: true, }, "src_address_type": { Type: schema.TypeString, Computed: true, }, "src_port": { Type: schema.TypeString, Computed: true, }, "src_mac_address": { Type: schema.TypeString, Computed: true, }, "tcp_flags": { Type: schema.TypeString, Computed: true, }, "tcp_mss": { Type: schema.TypeString, Computed: true, }, "time": { Type: schema.TypeString, Computed: true, }, "tls_host": { Type: schema.TypeString, Computed: true, }, "to_address": { Type: schema.TypeString, Computed: true, }, "to_ports": { Type: schema.TypeString, Computed: true, }, }, }, } } ================================================ FILE: routeros/datasource_ipv6_firewall_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testDatasourceIPv6Firewall = "data.routeros_ipv6_firewall.fw" func TestAccDatasourceIPv6FirewallTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccDatasourceIPv6FirewallConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testDatasourceIPv6Firewall), ), }, }, }) }) } } func testAccDatasourceIPv6FirewallConfig() string { return providerConfig + ` data "routeros_ipv6_firewall" "fw" { rules {} } ` } ================================================ FILE: routeros/datasource_system_resource.go ================================================ package routeros // Script generated from sampled device MikroTik 7.10 (stable) on CHR QEMU-x86_64 import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func DatasourceSystemResource() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/system/resource"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields( "cpu_frequency", "cpu_load", "free_hdd_space", "free_memory", "uptime", "write_sect_since_reboot", "write_sect_total", "bad_blocks", ), "architecture_name": { // Sample = architecture-name: "x86_64" Type: schema.TypeString, Computed: true, }, "board_name": { // Sample = board-name: "CHR" Type: schema.TypeString, Computed: true, }, "build_time": { // Sample = build-time: "Jun/15/2023 05:17:29" Type: schema.TypeString, Computed: true, }, "cpu": { // Sample = cpu: "QEMU" Type: schema.TypeString, Computed: true, }, "cpu_count": { // Sample = cpu-count: "4" Type: schema.TypeInt, Computed: true, }, "factory_software": { // Sample = factory-software: "7.1" Type: schema.TypeString, Computed: true, }, "platform": { // Sample = platform: "MikroTik" Type: schema.TypeString, Computed: true, }, "total_hdd_space": { // Sample = total-hdd-space: "93564928" Type: schema.TypeInt, Computed: true, }, "total_memory": { // Sample = total-memory: "469762048" Type: schema.TypeInt, Computed: true, }, "version": { // Sample = version: "7.10 (stable)" Type: schema.TypeString, Computed: true, }, } return &schema.Resource{ ReadContext: DefaultSystemDatasourceRead(resSchema), Schema: resSchema, } } ================================================ FILE: routeros/datasource_system_resource_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testDatasourceSystemResource = "data.routeros_system_resource.data" func TestAccDatasourceSystemResourceTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccDatasourceSystemResourceConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testDatasourceSystemResource), ), }, }, }) }) } } func testAccDatasourceSystemResourceConfig() string { return providerConfig + ` data "routeros_system_resource" "data" {} ` } ================================================ FILE: routeros/datasource_system_routerboard.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { "current-firmware":"7.11.2", "factory-firmware":"6.48.6", "firmware-type":"qca9531L", "model":"CRS312-4C+8XG", "revision":"r2", "routerboard":"true", "serial-number":"XXXXXXXXXX", "upgrade-firmware":"7.11.2" } */ // https://help.mikrotik.com/docs/display/ROS/ func DatasourceSystemRouterboard() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/system/routerboard"), MetaId: PropId(Id), "board_name": { Type: schema.TypeString, Computed: true, }, "current_firmware": { Type: schema.TypeString, Computed: true, }, "factory_firmware": { Type: schema.TypeString, Computed: true, }, "firmware_type": { Type: schema.TypeString, Computed: true, }, "model": { Type: schema.TypeString, Computed: true, }, "revision": { Type: schema.TypeString, Computed: true, }, "routerboard": { Type: schema.TypeBool, Computed: true, }, "serial_number": { Type: schema.TypeString, Computed: true, }, "upgrade_firmware": { Type: schema.TypeString, Computed: true, }, } return &schema.Resource{ ReadContext: DefaultSystemDatasourceRead(resSchema), Schema: resSchema, } } ================================================ FILE: routeros/datasource_system_routerboard_test.go ================================================ package routeros import ( "testing" ) const testDatasourceSystemRouterboard = "data.routeros_system_routerboard.data" func TestAccDatasourceSystemRouterboardTest_basic(t *testing.T) { t.Log("The test is skipped, the resource is only available on real hardware.") /* // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccDatasourceSystemRouterboardConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testDatasourceSystemRouterboard), ), }, }, }) }) } */ } /* func testAccDatasourceSystemRouterboardConfig() string { return providerConfig + ` data "routeros_system_routerboard" "data" {} ` } */ ================================================ FILE: routeros/datasource_wifi_easy_connect.go ================================================ package routeros import ( "bytes" "context" "crypto/sha1" "fmt" "regexp" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/mdp/qrterminal/v3" ) func DatasourceWiFiEasyConnect() *schema.Resource { return &schema.Resource{ ReadContext: datasourceQRGenerate, Schema: map[string]*schema.Schema{ "type": { Type: schema.TypeString, Optional: true, Description: "Authentication type; can be WEP or WPA or WPA2-EAP, or nopass for no password. " + "Or, omit for no password.", Default: "WPA", ValidateFunc: validation.StringInSlice([]string{"WEP", "WPA2", "WPA2-EAP", "nopass"}, false), }, "ssid": { Type: schema.TypeString, Required: true, Description: "Network SSID. Required. Enclose in double quotes if it is an ASCII name, but could " + "be interpreted as hex (i.e. \"ABCD\").", }, "password": { Type: schema.TypeString, Required: true, Sensitive: true, Description: "Password, ignored if T is nopass (in which case it may be omitted). Enclose in double " + "quotes if it is an ASCII name, but could be interpreted as hex (i.e. \"ABCD\").", }, "hidden": { Type: schema.TypeBool, Optional: true, Description: "True if the network SSID is hidden.", }, "eap_method": { Type: schema.TypeString, Optional: true, Description: "(WPA2-EAP only) EAP method, like TTLS or PWD.", ValidateFunc: validation.StringInSlice([]string{"TTLS", "PWD"}, false), }, "eap_anonymous": { Type: schema.TypeBool, Optional: true, Description: "(WPA2-EAP only) Anonymous identity", }, "eap_identity": { Type: schema.TypeString, Optional: true, Description: "(WPA2-EAP only) Identity.", }, "eap_phase2": { Type: schema.TypeString, Optional: true, Description: "(WPA2-EAP only) Phase 2 method, like `MSCHAPV2`", }, "qr_code": { Type: schema.TypeString, Computed: true, Description: "QR Code", }, }, } } var mecardEscape = regexp.MustCompile(`([;,":\\])`) func datasourceQRGenerate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { text := "WIFI:" switch d.Get("type").(string) { case "", "nopass": case "WPA2-EAP": text += fmt.Sprintf("E:%v;I:%v;PH2:%v;", d.Get("eap_method").(string), d.Get("eap_identity").(string), d.Get("eap_phase2").(string), ) if d.Get("eap_anonymous").(bool) { text += "A:anon;" } default: text += fmt.Sprintf("T:%v;P:%v;", d.Get("type").(string), mecardEscape.ReplaceAllString(d.Get("password").(string), "\\$1"), ) } // SSID text += fmt.Sprintf("S:%v;", mecardEscape.ReplaceAllString(d.Get("ssid").(string), "\\$1")) // Hidden if val := d.Get("hidden").(bool); val { text += fmt.Sprintf("H:%v;", val) } text += ";" d.SetId(fmt.Sprintf("%x", sha1.Sum([]byte(text)))) buf := bytes.NewBuffer(nil) qrterminal.GenerateWithConfig(text, qrterminal.Config{ Writer: buf, Level: qrterminal.L, QuietZone: 1, BlackChar: qrterminal.BLACK, WhiteChar: qrterminal.WHITE, }) d.Set("qr_code", buf.String()) return nil } ================================================ FILE: routeros/datasource_wifi_easy_connect_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testDatasourceWiFiEasyConnect = "data.routeros_wifi_easy_connect.test" func TestAccDatasourceWiFiEasyConnectTest_basic(t *testing.T) { t.Run("QR Code", func(t *testing.T) { resource.Test(t, resource.TestCase{ ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccDatasourceWiFiEasyConnectConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testDatasourceWiFiEasyConnect), ), }, }, }) }) } func testAccDatasourceWiFiEasyConnectConfig() string { return providerConfig + ` data "routeros_wifi_easy_connect" "test" { type = "WPA2" ssid = "test" password = "password12345" } ` } ================================================ FILE: routeros/datasource_x509.go ================================================ package routeros import ( "context" "crypto/sha256" "crypto/x509" "encoding/pem" "fmt" "regexp" "strings" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func DatasourceX509() *schema.Resource { return &schema.Resource{ ReadContext: datasourceParseCertificate, Schema: map[string]*schema.Schema{ "data": { Type: schema.TypeString, Required: true, Description: "X509 certificate in PEM format.", }, "id": { Type: schema.TypeString, Computed: true, }, "akid": { Type: schema.TypeString, Computed: true, }, "authority": { Type: schema.TypeBool, Computed: true, }, "common_name": { Type: schema.TypeString, Computed: true, }, "digest_algorithm": { Type: schema.TypeString, Computed: true, }, "fingerprint": { Type: schema.TypeString, Computed: true, }, "invalid_after": { Type: schema.TypeString, Computed: true, }, "invalid_before": { Type: schema.TypeString, Computed: true, }, "issuer": { Type: schema.TypeString, Computed: true, }, // "key_size": { // Type: schema.TypeString, // Computed: true, // }, "key_type": { Type: schema.TypeString, Computed: true, }, // "key_usage": { // Type: schema.TypeString, // Computed: true, // }, "serial_number": { Type: schema.TypeString, Computed: true, }, "signature_algorithm": { Type: schema.TypeString, Computed: true, }, "skid": { Type: schema.TypeString, Computed: true, }, "subject": { Type: schema.TypeString, Computed: true, }, "subject_alt_name": { Type: schema.TypeString, Computed: true, }, "version": { Type: schema.TypeInt, Computed: true, }, "pem": { Type: schema.TypeString, Computed: true, }, }, } } func datasourceParseCertificate(_ context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { re := regexp.MustCompile(`(?m)^\s+`) block, _ := pem.Decode(re.ReplaceAll([]byte(d.Get("data").(string)), nil)) if block == nil { return diag.Errorf("Invalid PEM content") } c, err := x509.ParseCertificate(block.Bytes) if err != nil { return diag.FromErr(err) } d.Set("akid", fmt.Sprintf("%x", c.AuthorityKeyId)) d.Set("authority", c.IsCA) d.Set("common_name", c.Subject.CommonName) d.Set("digest_algorithm", c.SignatureAlgorithm.String()) d.Set("fingerprint", fmt.Sprintf("%x", sha256.Sum256(c.Raw))) d.Set("invalid_after", c.NotAfter.String()) d.Set("invalid_before", c.NotBefore.String()) d.Set("issuer", c.Issuer.String()) d.Set("key_type", c.PublicKeyAlgorithm.String()) d.Set("issuer", c.Issuer.String()) d.Set("serial_number", c.SerialNumber.Text(16)) d.Set("skid", fmt.Sprintf("%x", c.SubjectKeyId)) d.Set("signature_algorithm", c.SignatureAlgorithm.String()) d.Set("subject", c.Subject.String()) d.Set("subject_alt_name", getSANs(c)) d.Set("version", c.Version) d.Set("pem", string(pem.EncodeToMemory(block))) d.SetId(c.SerialNumber.String()) return nil } func getSANs(c *x509.Certificate) string { var res []string for _, v := range c.DNSNames { res = append(res, fmt.Sprintf("DNS:%v", v)) } for _, v := range c.IPAddresses { res = append(res, fmt.Sprintf("IP:%v", v)) } for _, v := range c.EmailAddresses { res = append(res, fmt.Sprintf("EMAIL:%v", v)) } for _, v := range c.URIs { res = append(res, fmt.Sprintf("URI:%v", v)) } return strings.Join(res, ",") } ================================================ FILE: routeros/datasource_x509_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testDatasourceX509 = "data.routeros_x509.cert" func TestAccDatasourceX509Test_basic(t *testing.T) { t.Run("X509", func(t *testing.T) { resource.Test(t, resource.TestCase{ ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccDatasourceX509Config(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testDatasourceX509), ), }, }, }) }) } func testAccDatasourceX509Config() string { return providerConfig + ` data "routeros_x509" "cert" { data = < 0 && u.Query[len(u.Query) - 1] != "?#|" { // u.Query = append(u.Query, "?#|") //} return append(res, u.Query...) } // GetRestURL Returns the URL for the client func (u *URL) GetRestURL() string { q := strings.Join(u.Query, "&") if len(q) > 0 && q[0] != '?' { q = "?" + q } return u.Path + q } // EscapeChars peterGo https://groups.google.com/g/golang-nuts/c/NiQiAahnl5E/m/U60Sm1of-_YJ func EscapeChars(data []byte) []byte { var u = []byte(`\u0000`) //var u = []byte(`U+0000`) var res = make([]byte, 0, len(data)) for i, ch := range data { if ch < 0x20 { res = append(res, u...) hex.Encode(res[len(res)-2:], data[i:i+1]) continue } res = append(res, ch) } return res } // Obtain a version of RouterOS to automatically customize resource schemas. func GetRouterOSVersion(m interface{}) (string, diag.Diagnostics) { res, err := ReadItems(nil, "/system/resource", m.(Client)) if err != nil { return "", diag.FromErr(err) } // Resource not found. if len(*res) == 0 { return "", diag.Errorf("RouterOS version not found") } version, ok := (*res)[0]["version"] if !ok { return "", diag.Errorf("RouterOS version not found") } // d.d | d.d.d re := regexp.MustCompile(`^(\d+\.){1,2}\d+`) if !re.MatchString(version) { return "", diag.Errorf("RouterOS version not found") } return re.FindString(version), nil } ================================================ FILE: routeros/mikrotik_client_api.go ================================================ package routeros import ( "context" "fmt" "reflect" "strings" "github.com/go-routeros/routeros/v3" ) type ApiClient struct { ctx context.Context HostURL string Username string Password string Transport TransportType extra *ExtraParams *routeros.Client } var ( apiMethodName = map[crudMethod]string{ crudCreate: "/add", crudRead: "/print", crudUpdate: "/set", crudDelete: "/remove", crudPost: "/set", crudImport: "/import", crudSign: "/sign", crudSignViaScep: "/add-scep", crudRemove: "/remove", crudRevoke: "/issued-revoke", crudMove: "/move", crudStart: "/start", crudStop: "/stop", crudGenerateKey: "/generate-key", } ) func (c *ApiClient) GetExtraParams() *ExtraParams { return c.extra } func (c *ApiClient) GetTransport() TransportType { return c.Transport } func (c *ApiClient) SendRequest(method crudMethod, url *URL, item MikrotikItem, result interface{}) error { // https://help.mikrotik.com/docs/display/ROS/API // /interface/vlan/print + '?.id=*39' + '?type=vlan' cmd := url.GetApiCmd() // The first element is the Path cmd[0] += apiMethodName[method] // Marshal for fieldName, fieldValue := range item { cmd = append(cmd, fmt.Sprintf("=%s=%s", fieldName, fieldValue)) } ColorizedDebug(c.ctx, "request body: "+strings.Join(cmd, " ")) resp, err := c.RunArgs(cmd) if err != nil { return err } ColorizedDebug(c.ctx, "response body: "+resp.String()) if result == nil { return nil } // Unmarshal switch r := result.(type) { case *MikrotikItem: // Only ID returned. // !done @ [{`ret` `*7F`}] if len(resp.Re) == 0 { for k, v := range resp.Done.Map { (*r)[k] = v } break } // Fill in only one item. for k, v := range resp.Re[0].Map { (*r)[k] = v } case *[]MikrotikItem: // !re for _, sentence := range resp.Re { m := MikrotikItem{} for k, v := range sentence.Map { m[k] = v } *r = append(*r, m) } // !done @ [] is empty... default: panic("[SendRequest] type " + reflect.TypeOf(result).String() + " is not supported for API response unmarshaling.") } return nil } ================================================ FILE: routeros/mikrotik_client_rest.go ================================================ package routeros import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "reflect" "strings" ) type RestClient struct { ctx context.Context HostURL string Username string Password string Transport TransportType extra *ExtraParams *http.Client } type errorResponse struct { Detail string `json:"detail"` Error int `json:"error"` Message string `json:"message"` } var ( restMethodName = map[crudMethod]string{ crudCreate: "PUT", crudRead: "GET", crudUpdate: "PATCH", crudDelete: "DELETE", crudPost: "POST", crudImport: "POST", crudSign: "POST", crudSignViaScep: "POST", crudRemove: "POST", crudRevoke: "POST", crudMove: "POST", crudStart: "POST", crudStop: "POST", crudGenerateKey: "POST", } ) func (c *RestClient) GetExtraParams() *ExtraParams { return c.extra } func (c *RestClient) GetTransport() TransportType { return c.Transport } func (c *RestClient) SendRequest(method crudMethod, url *URL, item MikrotikItem, result interface{}) error { var data io.Reader if item != nil { b, err := json.Marshal(&item) if err != nil { return err } ColorizedDebug(c.ctx, "request body: "+string(b)) data = bytes.NewBuffer(b) } // https://mikrotik + /rest + /interface/vlan + ? + .id=*39 // Escaping spaces! requestUrl := c.HostURL + "/rest" + strings.Replace(url.GetRestURL(), " ", "%20", -1) ColorizedDebug(c.ctx, restMethodName[method]+" request URL: "+requestUrl) req, err := http.NewRequest(restMethodName[method], requestUrl, data) if err != nil { return err } req.Header.Set("Content-Type", "application/json") req.SetBasicAuth(c.Username, c.Password) res, err := c.Do(req) if err != nil { return err } defer func() { _ = res.Body.Close() }() body, _ := io.ReadAll(res.Body) if res.StatusCode < http.StatusOK || res.StatusCode >= http.StatusBadRequest { var errRes errorResponse ColorizedDebug(c.ctx, fmt.Sprintf("error response body:\n%s", body)) if err = json.Unmarshal(body, &errRes); err != nil { return fmt.Errorf("json.Unmarshal - %v", err) } else { return fmt.Errorf("%v '%v' returned response code: %v, message: '%v', details: '%v'", restMethodName[method], requestUrl, res.StatusCode, errRes.Message, errRes.Detail) } } ColorizedDebug(c.ctx, "response body: "+string(body)) // Cast single return values to an array. if !isJsonArray(body) { body = append([]byte{'['}, append(body, []byte{']'}...)...) } // If the requested value is a slice, then parse the JSON directly into it. var slice = new([]MikrotikItem) if result != nil && isSlice(result) { slice = result.(*[]MikrotikItem) } if len(body) != 0 && result != nil { if err = json.Unmarshal(body, &slice); err != nil { if e, ok := err.(*json.SyntaxError); ok { ColorizedDebug(c.ctx, fmt.Sprintf("json.Unmarshal(response body): syntax error at byte offset %d", e.Offset)) if err = json.Unmarshal(EscapeChars(body), &slice); err != nil { return fmt.Errorf("json.Unmarshal(response body): %v", err) } } else { return err } } } if result != nil && !isSlice(result) && len(*slice) > 0 { // result.(*MikrotikItem).replace(&(*slice)[0]) for k, v := range (*slice)[0] { (*result.(*MikrotikItem))[k] = v } } return nil } // isSlice The function returns information whether the passed parameter is a slice. // The incoming type is a variable or pointer. func isSlice(i any) bool { t := reflect.TypeOf(i) if t.Kind() == reflect.Pointer { t = t.Elem() } return t.Kind() == reflect.Slice } // isJsonArray The function returns information about the type of JSON response. // Based on the response, we can cast MT's response to an array of values. // After some time, we can say that it is easier to operate with an array of values, // since MT can return '[]' which is not obvious in the process of creating a single resource. func isJsonArray(b []byte) bool { b = bytes.TrimLeft(b, " \t\r\n") return len(b) > 0 && b[0] == '[' } ================================================ FILE: routeros/mikrotik_client_transport_test.go ================================================ package routeros import ( "context" "crypto/tls" "fmt" "net/http" "os" "testing" "time" "github.com/go-routeros/routeros/v3" ) func newApiClient(ctx context.Context, hostUrl, user, pass string, useTLS bool) (*ApiClient, error) { api := &ApiClient{ ctx: ctx, HostURL: hostUrl, Username: user, Password: pass, Transport: TransportAPI, } tlsConf := tls.Config{ InsecureSkipVerify: true, } var err error if useTLS { api.Client, err = routeros.DialTLS(api.HostURL, api.Username, api.Password, &tlsConf) } else { api.Client, err = routeros.Dial(api.HostURL, api.Username, api.Password) } if err != nil { return nil, err } api.Async() return api, nil } func newRestClient(ctx context.Context, hostUrl, user, pass string) *RestClient { return &RestClient{ ctx: ctx, HostURL: hostUrl, Username: user, Password: pass, Transport: TransportREST, Client: &http.Client{ Timeout: time.Minute, Transport: &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, }, }, }, } } func TestClientTransport_SendRequest(t *testing.T) { testAccPreCheck(t) ctx := context.Background() host := reHost.FindStringSubmatch(os.Getenv("ROS_HOSTURL"))[1] user := os.Getenv("ROS_USERNAME") pass := os.Getenv("ROS_PASSWORD") api, err := newApiClient(ctx, host+":8728", user, pass, false) if err != nil { t.Fatal(err) } apis, err := newApiClient(ctx, host+":8729", user, pass, true) if err != nil { t.Fatal(err) } rest := newRestClient(ctx, "https://"+host+":443", user, pass) type fields struct { Transport TransportType Client interface{} } type args struct { method crudMethod url *URL item MikrotikItem result interface{} } tests := []struct { name string fields fields args args wantErr bool }{ { name: "Test API connection", fields: fields{TransportAPI, api}, args: args{crudRead, &URL{Path: "/system/resource"}, nil, &[]MikrotikItem{}}, wantErr: false, }, { name: "Test APIs connection", fields: fields{TransportAPI, apis}, args: args{crudRead, &URL{Path: "/system/resource"}, nil, &[]MikrotikItem{}}, wantErr: false, }, { name: "Test REST connection", fields: fields{TransportREST, rest}, args: args{crudRead, &URL{Path: "/system/resource"}, nil, &MikrotikItem{}}, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := tt.fields.Client.(Client) if err := c.SendRequest(tt.args.method, tt.args.url, tt.args.item, tt.args.result); (err != nil) != tt.wantErr { t.Fatalf("SendRequest() error = %v, wantErr %v", err, tt.wantErr) } var info MikrotikItem if tt.fields.Transport == TransportAPI { if len(*tt.args.result.(*[]MikrotikItem)) == 0 { t.Fatalf("Response is empty.") } info = (*tt.args.result.(*[]MikrotikItem))[0] } else { info = *tt.args.result.(*MikrotikItem) } fmt.Printf("\t\t::: %v %v, RouterOS: %v at %v :::\n", info["platform"], info["board-name"], info["version"], info["build-time"]) }) } } ================================================ FILE: routeros/mikrotik_crud.go ================================================ package routeros import ( "context" "fmt" ) // resource path is '/interface/vlan' etc. // resource query is '/id' or '?.id=*39'. var ( errEmptyId = fmt.Errorf("the resource id not defined") errEmptyItem = fmt.Errorf("the item is null") errEmptyPath = fmt.Errorf("the resource path not defined") ) // https://help.mikrotik.com/docs/display/ROS/REST+API func CreateItem(ctx context.Context, item MikrotikItem, resourcePath string, c Client) (MikrotikItem, error) { if item == nil { return nil, errEmptyItem } if resourcePath == "" { return nil, errEmptyPath } var crud = crudCreate if cm := ctxGetCrudMethod(ctx); cm != crudUnknown { crud = cm if c.GetTransport() == TransportREST { // apiMethodName[crud] is CLI path resourcePath += apiMethodName[crud] } } res := MikrotikItem{} err := c.SendRequest(crud, &URL{Path: resourcePath}, item, &res) return res, err } func ReadItems(id *ItemId, resourcePath string, c Client) (*[]MikrotikItem, error) { // id can be empty. if resourcePath == "" { return nil, errEmptyPath } url := &URL{Path: resourcePath} // If the 'id' is nil, then this is a Datasource reading (resource Path only). if id != nil { // REST: prevent 404 'Not Found' error by direct resource request (/interface/vlan/*39). // Error occurs when a resource has been deleted outside terraform control. // But in the case below we have an empty [] or non-empty array [{...}]. // /interface/vlan?.id=*39 url.Query = []string{"?" + id.Type.String() + "=" + id.Value} } var res []MikrotikItem err := c.SendRequest(crudRead, url, nil, &res) return &res, err } func ReadItemsFiltered(filter []string, resourcePath string, c Client) (*[]MikrotikItem, error) { if resourcePath == "" { return nil, errEmptyPath } // Filter format: name=value // REST query: name=value; name=value // API query: ?=name=value; ?=name=value if c.GetTransport() == TransportAPI { for i, s := range filter { filter[i] = "?=" + s } } url := &URL{Path: resourcePath, Query: filter} var res []MikrotikItem err := c.SendRequest(crudRead, url, nil, &res) return &res, err } func UpdateItem(id *ItemId, resourcePath string, item MikrotikItem, c Client) (MikrotikItem, error) { if id.Value == "" { return nil, errEmptyId } if resourcePath == "" { return nil, errEmptyPath } if c.GetTransport() == TransportREST { // /interface/vlan/*39 resourcePath += "/" + id.Value } else { item[".id"] = id.Value } res := MikrotikItem{} err := c.SendRequest(crudUpdate, &URL{Path: resourcePath}, item, &res) return res, err } func DeleteItem(id *ItemId, resourcePath string, c Client) error { if id.Value == "" { return errEmptyId } if resourcePath == "" { return errEmptyPath } url := &URL{Path: resourcePath} if c.GetTransport() == TransportREST { // This method is used to delete the record with a specified ID from the menu encoded in the URL. // If the deletion has been succeeded, the server responds with an empty response. // For example, call to delete the record twice, on second call router will return 404 error. // /interface/vlan/*39 url.Path += "/" + id.Value } else { url.Query = []string{"=.id=" + id.Value} } return c.SendRequest(crudDelete, url, nil, &MikrotikItem{}) } ================================================ FILE: routeros/mikrotik_resource_drift.go ================================================ // Code generated by "tools/drift/main.go"; DO NOT EDIT. package routeros func init() { driftAttributeSlice.Add("7.1", "/ip/dhcp-server", "src_address", "server-address") driftAttributeSlice.Add("7.1", "/routing/ospf/area", "nssa_translate", "nssa-translator") driftAttributeSlice.Add("7.17", "/interface/wireguard/peers", "is_responder", "responder") driftAttributeSlice.Add("7.18", "/container", "tag", "repo") driftAttributeSlice.Add("7.19", "/routing/bgp/connection", "address_families", "afi") driftAttributeSlice.Add("7.19", "/routing/bgp/template", "address_families", "afi") driftAttributeSlice.Add("7.20", "/container", "envlist", "envlists") driftAttributeSlice.Add("7.20", "/container/config", "ram_high", "memory-high") driftAttributeSlice.Add("7.20", "/container/envs", "name", "list") driftAttributeSlice.Add("7.20", "/interface/vxlan", "vrf", "vtep-vrf") driftAttributeSlice.Add("7.21", "/ip/ssh", "always-allow-password-login", "password-authentication") driftAttributeSlice.SortDesc() } ================================================ FILE: routeros/mikrotik_resource_drift.yaml ================================================ 7.1: /ip/dhcp-server: - tf: src_address mt: server-address /routing/ospf/area: - tf: nssa_translate mt: nssa-translator 7.17: /interface/wireguard/peers: - tf: is_responder mt: responder 7.18: /container: - tf: tag mt: repo 7.19: /routing/bgp/connection: - tf: address_families mt: afi /routing/bgp/template: - tf: address_families mt: afi 7.20: /interface/vxlan: - tf: vrf mt: vtep-vrf /container: - tf: envlist mt: envlists /container/config: - tf: ram_high mt: memory-high /container/envs: - tf: name mt: list 7.21: /ip/ssh: - tf: always-allow-password-login mt: password-authentication ================================================ FILE: routeros/mikrotik_resource_drift_implementation.go ================================================ package routeros import ( "cmp" "fmt" "log" "slices" "strconv" "strings" ) var driftAttributeSlice driftObjects // Drift objects type driftObjects []driftObject type driftObject struct { Version uint64 ros string Resources map[string][]driftAttribute } type driftAttribute struct { TF string MT string } // Add an object to the slice. // ros - RouterOS version (7.17.2) // resourcePath - resource path // attrTF - attribute name in the latest schema version // attrMT - attribute name in MikroTik parameters func (do *driftObjects) Add(ros, resourcePath, attrTF, attrMT string) { version, err := parseRouterOSVersion(ros) if err != nil { log.Fatal(err) } if i := do.index(version); i != -1 { (*do)[i].Resources[resourcePath] = append((*do)[i].Resources[resourcePath], driftAttribute{attrTF, attrMT}) } else { *do = append(*do, driftObject{ Version: version, ros: ros, Resources: map[string][]driftAttribute{resourcePath: {{attrTF, attrMT}}}, }) } } // Sorting a slice in descending order. func (do *driftObjects) SortDesc() { slices.SortFunc(driftAttributeSlice, func(a, b driftObject) int { return cmp.Compare(a.Version, b.Version) * -1 }) } // Getting the object index by RouterOS version. func (do *driftObjects) index(version uint64) int { for i := range *do { if version == (*do)[i].Version { return i } } return -1 } // Obtaining a map to match TF attributes and MT parameters for further transformation. // Direct output (for TF to MT serialization) and reverse output (MT to TF) are provided. func (do *driftObjects) GetDriftMap(ros, resName string, reverse bool) (res map[string]string) { version, err := parseRouterOSVersion(ros) if err != nil { log.Fatal(err) } res = map[string]string{} for i := range *do { if version >= (*do)[i].Version { for _, attr := range (*do)[i].Resources[resName] { if !reverse { res[attr.TF] = attr.MT } else { res[attr.MT] = attr.TF } } } } return } // Parse RouterOS version. func parseRouterOSVersion(ros string) (version uint64, err error) { for i, p := range strings.Split(ros, ".") { var u uint64 if u, err = strconv.ParseUint(p, 10, 64); err != nil { err = fmt.Errorf("RouterOS version parts parsing error, %v", err) return } else { version += u << ((2 - i) * 8) } if i > 3 { break } } if version == 0 { err = fmt.Errorf("RouterOS version parsing error, version is zero") } return } ================================================ FILE: routeros/mikrotik_serialize.go ================================================ package routeros import ( "fmt" "regexp" "slices" "strconv" "strings" "sync" "time" "github.com/hashicorp/go-cty/cty" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) var reMetadataFields = regexp.MustCompile(`^___\S+___$`) var reTransformSet = regexp.MustCompile(`"\s*?(\S+?)\s*?\s*?:\s*?\s*?(\S+?)\s*?"`) var reSkipFields = regexp.MustCompile(`"\s*?(\S+?)\s*?"\s*?`) // GetMetadata Get item metadata fields from resource schema. func GetMetadata(s map[string]*schema.Schema) *MikrotikItemMetadata { meta := &MikrotikItemMetadata{} // Schema map iteration. for terraformSnakeName, terraformMetadata := range s { if reMetadataFields.MatchString(terraformSnakeName) { switch terraformSnakeName { case MetaId: meta.IdType = IdType(terraformMetadata.Default.(int)) case MetaResourcePath: meta.Path = terraformMetadata.Default.(string) default: if meta.Meta == nil { meta.Meta = make(map[string]string) } meta.Meta[terraformSnakeName] = terraformMetadata.Default.(string) } } } return meta } func isEmpty(propName string, schemaProp *schema.Schema, d *schema.ResourceData, confValue cty.Value) bool { v := d.Get(propName) switch schemaProp.Type { case schema.TypeString: if schemaProp.Default != nil { return v.(string) == "" && schemaProp.Default.(string) == "" } return v.(string) == "" && confValue.IsNull() case schema.TypeFloat, schema.TypeInt: return confValue.IsNull() && schemaProp.Default == nil case schema.TypeBool: // If true, it is always not empty: if v.(bool) { return false } // Use the default value: if schemaProp.Default != nil { return false } return confValue.IsNull() case schema.TypeList: if confValue.Type().ElementType().IsObjectType() { return len(v.([]interface{})) == 0 } return len(v.([]interface{})) == 0 && confValue.IsNull() case schema.TypeSet: return v.(*schema.Set).Len() == 0 && confValue.IsNull() case schema.TypeMap: return len(v.(map[string]interface{})) == 0 default: panic("[isEmpty] wrong resource type: " + schemaProp.Type.String()) } } // loadTransformSet Converting the metadata of the 'MetaTransformSet' field into a map for forward and reverse // transformation. This map should be applied before the logical processing of fields at the beginning of // serialization/deserialization. // Forward transformation for use in the 'MikrotikResourceDataToTerraform' function, reverse transformation for use // in the 'TerraformResourceDataToMikrotik' function. // s: "channel.config: channel","datapath.config: datapath"` in the Mikrotik (kebab) notation! func loadTransformSet(s string, reverse bool) (m map[string]string) { m = make(map[string]string) for _, b := range reTransformSet.FindAllStringSubmatch(s, -1) { if !reverse { m[b[1]] = b[2] } else { m[b[2]] = b[1] } } return } // loadSkipFields A list of fields that will not be serialized and transferred to Mikrotik. func loadSkipFields(s string) (m map[string]struct{}) { m = make(map[string]struct{}) for _, b := range reSkipFields.FindAllStringSubmatch(s, -1) { m[b[1]] = struct{}{} } return } // ListToString Convert List and Set to a delimited string. func ListToString(v any) (res string) { for i, elem := range v.([]interface{}) { if i > 0 { res += fmt.Sprintf(",%v", elem) } else { res = fmt.Sprint(elem) } } return } // TerraformResourceDataToMikrotik Marshal Mikrotik resource from TF resource schema. func TerraformResourceDataToMikrotik(s map[string]*schema.Schema, d *schema.ResourceData) (MikrotikItem, *MikrotikItemMetadata) { item := MikrotikItem{} meta := &MikrotikItemMetadata{} rawConfig := d.GetRawConfig() var transformSet map[string]string var skipFields, setUnsetFields map[string]struct{} // {"channel.config: channel", "datapath.config: datapath", "schema-field-name": "mikrotik-field-name"} if ts, ok := s[MetaTransformSet]; ok { transformSet = loadTransformSet(ts.Default.(string), false) } // Resource attribute drift compensation. if drift := driftAttributeSlice.GetDriftMap(RouterOSVersion, s[MetaResourcePath].Default.(string), false); len(drift) > 0 { if transformSet == nil { transformSet = make(map[string]string) } for k, v := range drift { transformSet[k] = v } } // "field_first", "field_second", "field_third" if sf, ok := s[MetaSkipFields]; ok { skipFields = loadSkipFields(sf.Default.(string)) } if suf, ok := s[MetaSetUnsetFields]; ok { setUnsetFields = loadSkipFields(suf.Default.(string)) } // Schema map iteration. for terraformSnakeName, terraformMetadata := range s { // Fill in the metadata fields. if reMetadataFields.MatchString(terraformSnakeName) { switch terraformSnakeName { case MetaId: meta.IdType = IdType(terraformMetadata.Default.(int)) case MetaResourcePath: meta.Path = terraformMetadata.Default.(string) case MetaTransformSet, MetaSkipFields, MetaSetUnsetFields, MetaDropByValue: continue default: meta.Meta[terraformSnakeName] = terraformMetadata.Default.(string) } continue } // Skip the fields specified in the schema. if _, ok := skipFields[terraformSnakeName]; ok { continue } // Skip all read-only properties. if terraformMetadata.Computed && !terraformMetadata.Optional { continue } /* Skip all empty Optional fields. This logic may be broken, but I don't have enough examples to test it. All fields checked in Winbox must be Optional: true & Computed: true. Otherwise, there will be an error: "After applying this test step, the plan was not empty." Terraform will perform the following actions: # routeros_interface_bridge.test_bridge will be updated in-place ~ resource "routeros_interface_bridge" "test_bridge" { - fast_forward = true -> null id = "*DD" name = "test_bridge" # (22 unchanged attributes hidden) } */ /* old, new := d.GetChange(terraformSnakeName) conf := d.GetRawConfig().GetAttr(terraformSnakeName).IsNull() fmt.Println(rawConfig.GetAttr(terraformSnakeName).IsKnown()) fmt.Printf("%25s - old: '%10v', new: '%10v', isNull: %v", terraformSnakeName, old, new, conf) */ if terraformMetadata.Optional && !d.HasChange(terraformSnakeName) && isEmpty(terraformSnakeName, s[terraformSnakeName], d, rawConfig.GetAttr(terraformSnakeName)) { // fmt.Println(" ... skipped") continue } // fmt.Println() // terraformSnakeName = fast_forward, schemaPropData = true // NewMikrotikItem.Fields["fast-forward"] = "true" mikrotikKebabName := SnakeToKebab(terraformSnakeName) value := d.Get(terraformSnakeName) // WiFi basic_rates_ag -> basic-rates-a/g if transformSet != nil && terraformMetadata.Type != schema.TypeMap { if new, ok := transformSet[terraformSnakeName]; ok { mikrotikKebabName = SnakeToKebab(new) } } switch terraformMetadata.Type { case schema.TypeString: if _, ok := setUnsetFields[terraformSnakeName]; ok && value.(string) == "" { // Unset item["!"+mikrotikKebabName] = "" continue } item[mikrotikKebabName] = value.(string) case schema.TypeFloat: item[mikrotikKebabName] = strconv.FormatFloat(value.(float64), 'f', -1, 64) case schema.TypeInt: item[mikrotikKebabName] = strconv.Itoa(value.(int)) case schema.TypeBool: // true: {...,"interfaces":"ether3","passive":"","priority":"128",...} // false: {...,"interfaces":"ether3", "priority":"128",...} if _, ok := setUnsetFields[terraformSnakeName]; ok { if value.(bool) { item[mikrotikKebabName] = "" } else { // Unset item["!"+mikrotikKebabName] = "" } continue } item[mikrotikKebabName] = BoolToMikrotikJSON(value.(bool)) // Used to represent an ordered collection of items. case schema.TypeList: switch terraformMetadata.Elem.(type) { case *schema.Schema: item[mikrotikKebabName] = ListToString(value) case *schema.Resource: // skip if object is empty if value.([]interface{})[0] == nil { continue } list := value.([]interface{})[0].(map[string]interface{}) ctyList := rawConfig.GetAttr(terraformSnakeName).AsValueSlice()[0] for fieldName, value := range list { // "output.0.affinity" fieldNameInState := fmt.Sprintf("%v.%v.%v", terraformSnakeName, 0, fieldName) fieldSchema := terraformMetadata.Elem.(*schema.Resource).Schema[fieldName] // Skip all read-only properties. if fieldSchema.Computed && !fieldSchema.Optional { continue } if fieldSchema.Optional && !d.HasChange(fieldNameInState) && isEmpty(fieldNameInState, fieldSchema, d, ctyList.GetAttr(fieldName)) { continue } fieldName = SnakeToKebab(mikrotikKebabName + "." + fieldName) // Serialization of sets. if fieldSchema.Type == schema.TypeSet { value = ListToString(value.(*schema.Set).List()) } switch value := value.(type) { case string: item[fieldName] = value case int: item[fieldName] = strconv.Itoa(value) case bool: item[fieldName] = BoolToMikrotikJSON(value) } } } // Used to represent an unordered collection of items. case schema.TypeSet: if _, ok := setUnsetFields[terraformSnakeName]; ok { // policy = ["api", "read", "winbox"] -> ["api", "read"] // old = ... read, !telnet, !rommon, api, !local, winbox // new = api, read var res []string for _, v := range d.GetRawConfig().GetAttr(terraformSnakeName).AsValueSet().Values() { res = append(res, v.AsString()) } old, _ := d.GetChange(terraformSnakeName) for _, v := range old.(*schema.Set).List() { elem := v.(string) if len(elem) > 0 && elem[0] != '!' && !slices.Contains(res, elem) { res = append(res, "!"+elem) } } item[mikrotikKebabName] = strings.Join(res, ",") continue } item[mikrotikKebabName] = ListToString(value.(*schema.Set).List()) case schema.TypeMap: for k, v := range value.(map[string]interface{}) { // channel + "." + config k = SnakeToKebab(mikrotikKebabName + "." + k) // Field transformation: "channel.config" ---> "channel". if transformSet != nil { if new, ok := transformSet[k]; ok { k = new } } // Conversion of boolean values. s := BoolToMikrotikJSONStr(v.(string)) item[k] = s } default: panic(fmt.Sprintf("[TerraformResourceDataToMikrotik] resource type not implemented: %v for '%v'", terraformMetadata.Type, terraformSnakeName)) } } return item, meta } // MikrotikResourceDataToTerraform Unmarshal Mikrotik resource (incoming data: JSON, etc.) to TF resource schema. func MikrotikResourceDataToTerraform(item MikrotikItem, s map[string]*schema.Schema, d *schema.ResourceData) diag.Diagnostics { var diags diag.Diagnostics var err error var transformSet map[string]string var setUnsetFields, skipFields, dropByValue map[string]struct{} // {"channel": "channel.config", "mikrotik-field-name": "schema-field-name"} if ts, ok := s[MetaTransformSet]; ok { transformSet = loadTransformSet(ts.Default.(string), true) } // Resource attribute drift compensation. if drift := driftAttributeSlice.GetDriftMap(RouterOSVersion, s[MetaResourcePath].Default.(string), true); len(drift) > 0 { if transformSet == nil { transformSet = make(map[string]string) } for k, v := range drift { transformSet[k] = v } } // "field_first", "field_second", "field_third" if suf, ok := s[MetaSetUnsetFields]; ok { setUnsetFields = loadSkipFields(suf.Default.(string)) } if sf, ok := s[MetaSkipFields]; ok { skipFields = loadSkipFields(sf.Default.(string)) } if dbv, ok := s[MetaDropByValue]; ok { dropByValue = loadSkipFields(dbv.Default.(string)) } // TypeMap,TypeSet initialization information storage. var maps = make(map[string]map[string]interface{}) var nestedLists = make(map[string]map[string]interface{}) // Incoming map iteration. for mikrotikKebabName, mikrotikValue := range item { // Set the ID. //if mikrotikKebabName == ".id" { // if err = d.Set(KeyId, mikrotikValue); err != nil { // diags = append(diags, diag.FromErr(err)...) // } // continue //} // Skip all service fields (i.e. `.id`, `.nextid`, `ret` ...). if mikrotikKebabName[0:1] == "." || mikrotikKebabName == "ret" { continue } if _, ok := dropByValue[mikrotikValue]; ok { continue } // Mikrotik fields transformation: "channel" ---> "channel.config". // For further use in the map. if transformSet != nil { if new, ok := transformSet[mikrotikKebabName]; ok { mikrotikKebabName = new } } // field-name => field_name terraformSnakeName := KebabToSnake(mikrotikKebabName) if skipFields != nil { if _, ok := skipFields[terraformSnakeName]; ok { continue } } // Composite fields. var subFieldSnakeName string if strings.Contains(terraformSnakeName, ".") { f := strings.SplitN(terraformSnakeName, ".", 2) terraformSnakeName, subFieldSnakeName = f[0], f[1] } if _, ok := s[terraformSnakeName]; !ok { // For development. // panic("[MikrotikResourceDataToTerraform] The field was lost during the Schema development: " + terraformSnakeName) diags = append(diags, diag.Diagnostic{ // TODO Waiting for TestStep.ExpectWarning https://github.com/hashicorp/terraform-plugin-testing/pull/17 // The test response to Warnings has not yet been implemented. Severity: diag.Warning, Summary: "Field '" + terraformSnakeName + "' not found in the schema", Detail: fmt.Sprintf("[MikrotikResourceDataToTerraform] The field was lost during the Schema development: ▷ '%s': '%s' ◁", terraformSnakeName, mikrotikValue), }) // Catch all fields. continue } switch s[terraformSnakeName].Type { case schema.TypeString: err = d.Set(terraformSnakeName, mikrotikValue) case schema.TypeFloat: f, e := strconv.ParseFloat(mikrotikValue, 64) if e != nil { diags = diag.Errorf("%v for '%v' field", e, terraformSnakeName) break } err = d.Set(terraformSnakeName, f) case schema.TypeInt: i, e := strconv.Atoi(mikrotikValue) if e != nil { diags = diag.Errorf("%v for '%v' field", e, terraformSnakeName) break } err = d.Set(terraformSnakeName, i) case schema.TypeBool: if _, ok := setUnsetFields[terraformSnakeName]; ok { err = d.Set(terraformSnakeName, true) break } err = d.Set(terraformSnakeName, BoolFromMikrotikJSON(mikrotikValue)) case schema.TypeList, schema.TypeSet: var l []interface{} // Don't fill in empty strings (preventing a non-empty plan). // | # routeros_interface_wireguard_peer.wg_peer will be updated in-place // | ~ resource "routeros_interface_wireguard_peer" "wg_peer" { // | ~ allowed_address = [ // | - "", // | ] // | id = "*2" // | # (7 unchanged attributes hidden) // | } // Flat Lists & Sets: if _, ok := s[terraformSnakeName].Elem.(*schema.Schema); mikrotikValue != "" && ok { for _, v := range strings.Split(mikrotikValue, ",") { switch s[terraformSnakeName].Elem.(*schema.Schema).Type { case schema.TypeFloat: f, err := strconv.ParseFloat(v, 64) if err != nil { diags = diag.Errorf("%v for '%v' field", err, terraformSnakeName) continue } l = append(l, f) case schema.TypeInt: i, err := strconv.Atoi(v) if err != nil { diags = diag.Errorf("%v for '%v' field", err, terraformSnakeName) continue } l = append(l, i) default: l = append(l, v) } } if err != nil { break // case } } if s[terraformSnakeName].Type == schema.TypeList { switch s[terraformSnakeName].Elem.(type) { case *schema.Schema: err = d.Set(terraformSnakeName, l) case *schema.Resource: var v any if _, ok := s[terraformSnakeName].Elem.(*schema.Resource).Schema[subFieldSnakeName]; !ok { diags = append(diags, diag.Diagnostic{ Severity: diag.Warning, Summary: "Field '" + terraformSnakeName + "." + subFieldSnakeName + "' not found in the schema", Detail: fmt.Sprintf("[MikrotikResourceDataToTerraform] the datasource Schema sub-field was lost during development: ▷ '%s.%s' ◁", terraformSnakeName, subFieldSnakeName), }) continue } switch s[terraformSnakeName].Elem.(*schema.Resource).Schema[subFieldSnakeName].Type { case schema.TypeString: v = mikrotikValue case schema.TypeFloat: v, err = strconv.ParseFloat(mikrotikValue, 64) if err != nil { diags = diag.Errorf("%v for '%v.%v' field", err, terraformSnakeName, subFieldSnakeName) } case schema.TypeInt: v, err = strconv.Atoi(mikrotikValue) if err != nil { diags = diag.Errorf("%v for '%v.%v' field", err, terraformSnakeName, subFieldSnakeName) } case schema.TypeBool: v = BoolFromMikrotikJSON(mikrotikValue) case schema.TypeSet: var nl []interface{} // Nested list. for _, v := range strings.Split(mikrotikValue, ",") { switch s[terraformSnakeName].Elem.(*schema.Resource).Schema[subFieldSnakeName].Elem.(*schema.Schema).Type { case schema.TypeFloat: f, err := strconv.ParseFloat(v, 64) if err != nil { diags = diag.Errorf("%v for '%v' field", err, terraformSnakeName) continue } nl = append(nl, f) case schema.TypeInt: i, err := strconv.Atoi(v) if err != nil { diags = diag.Errorf("%v for '%v' field", err, terraformSnakeName) continue } nl = append(nl, i) default: nl = append(nl, v) } } v = nl } if err != nil { break } if list, ok := nestedLists[terraformSnakeName]; !ok { nestedLists[terraformSnakeName] = map[string]interface{}{subFieldSnakeName: v} } else { list[subFieldSnakeName] = v } } } else { err = d.Set(terraformSnakeName, schema.NewSet(schema.HashSchema(s[terraformSnakeName].Elem.(*schema.Schema)), l)) } case schema.TypeMap: // "yes" -> "true"; "no" -> "false" mikrotikValue = BoolFromMikrotikJSONStr(mikrotikValue) if m, ok := maps[terraformSnakeName]; !ok { // Create a new map when processing the first incoming field. maps[terraformSnakeName] = map[string]interface{}{subFieldSnakeName: mikrotikValue} } else { m[subFieldSnakeName] = mikrotikValue } default: // For development. //panic(fmt.Sprintf("[MikrotikResourceDataToTerraform] resource type not implemented: %v for '%v'", // s[terraformSnakeName].Type.String(), mikrotikValue)) diags = append(diags, diag.Diagnostic{ Severity: diag.Warning, Summary: "Can't fill the schema field", Detail: fmt.Sprintf("Resource type not implemented: '%v' for '%v'", s[terraformSnakeName].Type.String(), terraformSnakeName), }) } if err != nil { diags = append(diags, diag.FromErr(err)...) } } // Lists processing. for name, list := range nestedLists { if err = d.Set(name, []interface{}{list}); err != nil { diags = append(diags, diag.FromErr(err)...) } } // Maps processing. for name, m := range maps { if err = d.Set(name, m); err != nil { diags = append(diags, diag.FromErr(err)...) } } return diags } func MikrotikResourceDataToTerraformDatasource(items *[]MikrotikItem, resourceDataKeyName string, s map[string]*schema.Schema, d *schema.ResourceData) diag.Diagnostics { var diags diag.Diagnostics var dsItems []map[string]interface{} // System resource have an empty 'resourceDataKeyName'. var isSystemDatasource bool = (resourceDataKeyName == "") var transformSet map[string]string var skipFields map[string]struct{} // Resource attribute drift compensation. if drift := driftAttributeSlice.GetDriftMap(RouterOSVersion, s[MetaResourcePath].Default.(string), true); len(drift) > 0 { if transformSet == nil { transformSet = make(map[string]string) } for k, v := range drift { transformSet[k] = v } } if sf, ok := s[MetaSkipFields]; ok { skipFields = loadSkipFields(sf.Default.(string)) } // Checking the schema. if !isSystemDatasource { sv, ok := s[resourceDataKeyName] if !ok { // For development. //panic("[MikrotikResourceDataToTerraformDatasource] the datasource Schema field was lost during development: " + resourceDataKeyName) diags = append(diags, diag.Diagnostic{ Severity: diag.Warning, Summary: "Field '" + resourceDataKeyName + "' not found in the schema", Detail: fmt.Sprintf("[MikrotikResourceDataToTerraformDatasource] the datasource Schema field was lost during development: ▷ '%s' ◁", resourceDataKeyName), }) // Or panic. return diags } s = sv.Elem.(*schema.Resource).Schema } if isSystemDatasource && len(*items) != 1 { diags = append(diags, diag.Diagnostic{ Severity: diag.Error, Summary: "System resources should not return an array of values", Detail: fmt.Sprintf("[MikrotikResourceDataToTerraformDatasource] system resource '%s' polling returned %d values", s[MetaResourcePath].Default.(string), len(*items)), }) return diags } // Array of Mikrotik items iteration. for _, item := range *items { dsItem := map[string]interface{}{} // Incoming map iteration. for mikrotikKebabName, mikrotikValue := range item { // MT can return the field name in uppercase format. mikrotikKebabName = strings.ToLower(mikrotikKebabName) // In this case the ID must be a string. if mikrotikKebabName == ".id" { dsItem["id"] = mikrotikValue continue } // Skip all service fields. if mikrotikKebabName[0:1] == "." { continue } // Mikrotik fields transformation: "channel" ---> "channel.config". // For further use in the map. if transformSet != nil { if new, ok := transformSet[mikrotikKebabName]; ok { mikrotikKebabName = new } } // field-name => field_name terraformSnakeName := KebabToSnake(mikrotikKebabName) if skipFields != nil { if _, ok := skipFields[terraformSnakeName]; ok { continue } } if _, ok := s[terraformSnakeName]; !ok { // For development. //panic("[MikrotikResourceDataToTerraformDatasource] the field was lost during development.: " + terraformSnakeName) diags = append(diags, diag.Diagnostic{ Severity: diag.Warning, Summary: "Field '" + terraformSnakeName + "' not found in the schema", Detail: fmt.Sprintf("[MikrotikResourceDataToTerraformDatasource] the field was lost during the Schema development: ▷ '%s': '%s' ◁", terraformSnakeName, mikrotikValue), }) // Catch all fields. continue } var propValue interface{} switch s[terraformSnakeName].Type { case schema.TypeString: propValue = mikrotikValue case schema.TypeFloat: f, err := strconv.ParseFloat(mikrotikValue, 64) if err != nil { diags = append(diags, diag.Errorf("%v for '%v' field", err, terraformSnakeName)...) continue } propValue = f case schema.TypeInt: i, err := strconv.Atoi(mikrotikValue) if err != nil { diags = append(diags, diag.Errorf("%v for '%v' field", err, terraformSnakeName)...) continue } propValue = i case schema.TypeBool: // TODO Add support for set/unset fields? propValue = BoolFromMikrotikJSON(mikrotikValue) case schema.TypeList: var l []interface{} if mikrotikValue != "" { for _, s := range strings.Split(mikrotikValue, ",") { l = append(l, s) } } propValue = l // TODO Add processing of missing types: List(int), Set, Map case schema.TypeSet: var l []interface{} if mikrotikValue != "" { for _, s := range strings.Split(mikrotikValue, ",") { l = append(l, s) } } // String sets only (schema.HashString)! propValue = schema.NewSet(schema.HashString, l) default: // For development. //panic(fmt.Sprintf("Resource type not implemented: %v for '%v'", // s[terraformSnakeName].Type.String(), mikrotikValue)) diags = append(diags, diag.Diagnostic{ Severity: diag.Warning, Summary: "Can't fill the schema field", Detail: fmt.Sprintf("Resource type not implemented: %v for '%v'", s[terraformSnakeName].Type.String(), mikrotikValue), }) } dsItem[terraformSnakeName] = propValue } dsItems = append(dsItems, dsItem) } d.SetId(UniqueId()) if !isSystemDatasource { if err := d.Set(resourceDataKeyName, dsItems); err != nil { diags = append(diags, diag.FromErr(err)...) } } else { for k, v := range dsItems[0] { if err := d.Set(k, v); err != nil { diags = append(diags, diag.FromErr(err)...) } } } return diags } // Copied from terraform-plugin-testing@v1.2.0/helper/resource/id.go // Because this functionality is marked deprecated. const UniqueIdPrefix = `terraform-` // idCounter is a monotonic counter for generating ordered unique ids. var idMutex sync.Mutex var idCounter uint32 func UniqueId() string { return PrefixedUniqueId(UniqueIdPrefix) } func PrefixedUniqueId(prefix string) string { // Be precise to 4 digits of fractional seconds, but remove the dot before the // fractional seconds. timestamp := strings.Replace( time.Now().UTC().Format("20060102150405.0000"), ".", "", 1) idMutex.Lock() defer idMutex.Unlock() idCounter++ return fmt.Sprintf("%s%s%08x", prefix, timestamp, idCounter) } ================================================ FILE: routeros/mikrotik_serialize_test.go ================================================ package routeros import ( "reflect" "testing" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) var ( testResource = schema.Resource{ Schema: map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/test/resource"), MetaId: PropId(Id), "string": { Type: schema.TypeString, }, "float": { Type: schema.TypeFloat, }, "int": { Type: schema.TypeInt, }, "bool": { Type: schema.TypeBool, }, "computed": { Type: schema.TypeBool, Computed: true, }, }, } testDatasource = schema.Resource{ Schema: map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/test/resource"), MetaId: PropId(Id), "test_name": { Type: schema.TypeList, Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "string": { Type: schema.TypeString, }, "float": { Type: schema.TypeFloat, }, "int": { Type: schema.TypeInt, }, "bool": { Type: schema.TypeBool, }, }, }, }, }, } ) func Test_mikrotikResourceDataToTerraform(t *testing.T) { testItem := MikrotikItem{".id": "*39", "string": "string12345", "float": "0.01", "int": "10", "bool": "true"} testResourceData := testResource.TestResourceData() expectedRes := map[string]interface{}{"string": "string12345", "float": 0.01, "int": 10, "bool": true} err := MikrotikResourceDataToTerraform(testItem, testResource.Schema, testResourceData) if err != nil { t.Errorf("decoding err: %v", err) } for key, expected := range expectedRes { actual := testResourceData.Get(key) if !reflect.DeepEqual(actual, expected) { t.Fatalf("bad: expected:%#v\nactual:%#v", expected, actual) } } } func Test_terraformResourceDataToMikrotik(t *testing.T) { expected := MikrotikItem{"string": "string12345", "float": "0.01", "int": "10", "bool": "yes"} testResourceData := testResource.TestResourceData() testResourceData.SetId("*39") testResourceData.Set("string", "string12345") testResourceData.Set("float", 0.01) testResourceData.Set("int", 10) testResourceData.Set("bool", true) actual, _ := TerraformResourceDataToMikrotik(testResource.Schema, testResourceData) if !reflect.DeepEqual(actual, expected) { t.Fatalf("bad: expected:%#v\nactual:%#v", expected, actual) } } func Test_mikrotikResourceDataToTerraformDatasource(t *testing.T) { testItems := []MikrotikItem{ {"string": "string12345", "float": "0.01", "int": "10", "bool": "yes"}, {"string": "12345string", "float": "0.02", "int": "20", "bool": "no"}, } testResourceData := testDatasource.TestResourceData() expectedRes := []map[string]interface{}{ {MetaResourcePath: "", MetaId: 0, "string": "string12345", "float": 0.01, "int": 10, "bool": true}, {MetaResourcePath: "", MetaId: 0, "string": "12345string", "float": 0.02, "int": 20, "bool": false}, } err := MikrotikResourceDataToTerraformDatasource(&testItems, "test_name", testDatasource.Schema, testResourceData) if err != nil { t.Errorf("decoding err: %v", err) } for i, rec := range testResourceData.Get("test_name").([]interface{}) { for key, actual := range rec.(map[string]interface{}) { if !reflect.DeepEqual(actual, expectedRes[i][key]) { t.Fatalf("bad: (key: %v) expected:%#v\tactual:%#v", key, expectedRes[i][key], actual) } } } } func Test_loadTransformSet(t *testing.T) { testData := []struct { s string reverse bool }{ {toQuotedCommaSeparatedString("channel: channel.config", "datapath: datapath.config"), false}, {toQuotedCommaSeparatedString("mikrotik-field-name : schema-field-name"), false}, {toQuotedCommaSeparatedString("channel: channel.config", "datapath: datapath.config"), true}, {toQuotedCommaSeparatedString("mikrotik-field-name:schema-field-name"), true}, } expected := []map[string]string{ {"channel": "channel.config", "datapath": "datapath.config"}, {"mikrotik-field-name": "schema-field-name"}, {"channel.config": "channel", "datapath.config": "datapath"}, {"schema-field-name": "mikrotik-field-name"}, } for i, actual := range testData { if !reflect.DeepEqual(loadTransformSet(actual.s, actual.reverse), expected[i]) { t.Fatalf("bad: (item: %v) expected:%#v\tactual:%#v", i, expected[i], loadTransformSet(actual.s, actual.reverse)) } } } func Test_loadSkipFields(t *testing.T) { testData := []struct { s string }{ {toQuotedCommaSeparatedString("name")}, {toQuotedCommaSeparatedString("name", "rx_1024_1518", "rx_128_255", "rx_1519_max", "rx_256_511", "rx_512_1023", "rx_64")}, } expected := []map[string]struct{}{ {"name": struct{}{}}, {"name": struct{}{}, "rx_1024_1518": struct{}{}, "rx_128_255": struct{}{}, "rx_1519_max": struct{}{}, "rx_256_511": struct{}{}, "rx_512_1023": struct{}{}, "rx_64": struct{}{}}, } for i, actual := range testData { if !reflect.DeepEqual(loadSkipFields(actual.s), expected[i]) { t.Fatalf("bad: (item: %v) expected:%#v\tactual:%#v", i, expected[i], loadSkipFields(actual.s)) } } } ================================================ FILE: routeros/mikrotik_test.go ================================================ package routeros import ( "testing" ) func TestBoolFromMikrotikJSON(t *testing.T) { type args struct { s string } tests := []struct { name string args args want bool }{ { `Go bool from Mikrotik JSON - "true"`, args{"true"}, true, }, { `Go bool from Mikrotik JSON - "false"`, args{"false"}, false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := BoolFromMikrotikJSON(tt.args.s); got != tt.want { t.Errorf("BoolFromMikrotikJSON() = %v, want %v", got, tt.want) } }) } } func TestBoolToMikrotikJSON(t *testing.T) { type args struct { b bool } tests := []struct { name string args args want string }{ { `Go bool to Mikrotik JSON - "true"`, args{true}, "yes", }, { `Go bool to Mikrotik JSON - "false"`, args{false}, "no", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := BoolToMikrotikJSON(tt.args.b); got != tt.want { t.Errorf("BoolToMikrotikJSON() = %v, want %v", got, tt.want) } }) } } func TestMikrotikItem_GetID(t *testing.T) { tests := []struct { name string mi MikrotikItem want string }{ { "Get Mikrotik Item ID", MikrotikItem{".id": "*39"}, "*39", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := tt.mi.GetID(Id); got != tt.want { t.Errorf("GetID() = %v, want %v", got, tt.want) } }) } } func Test_kebabToSnake(t *testing.T) { type args struct { name string } tests := []struct { name string args args want string }{ { "Kebab to snake case", args{"kebab-to-snake"}, "kebab_to_snake", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := KebabToSnake(tt.args.name); got != tt.want { t.Errorf("KebabToSnake() = %v, want %v", got, tt.want) } }) } } func Test_snakeToKebab(t *testing.T) { type args struct { name string } tests := []struct { name string args args want string }{ { "Snake to kebab case", args{"snake_to_kebab"}, "snake-to-kebab", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := SnakeToKebab(tt.args.name); got != tt.want { t.Errorf("SnakeToKebab() = %v, want %v", got, tt.want) } }) } } ================================================ FILE: routeros/parse_data_information_units.go ================================================ package routeros import ( "fmt" "strconv" ) var bitUnitMap = map[byte]uint64{ 'K': 1e3, 'M': 1e6, 'G': 1e9, 'T': 1e12, 'P': 1e15, 'E': 1e18, } func ParseBitValues(s string) (uint64, error) { var unit uint64 = 1 // Special case: if all that is left is "0", this is zero. if s == "0" { return 0, nil } if s == "" { return 0, fmt.Errorf(`bits: invalid value "%v"`, s) } // Cut 'bps'. if l := len(s); l > 4 && s[l-3:] == "bps" { s = s[:l-3] } // Verifying the value. for i := 0; i < len(s); i++ { if s[i] < '0' || s[i] > '9' { // The last symbol. if i == len(s)-1 { var ok bool unit, ok = bitUnitMap[s[i]] if !ok { return 0, fmt.Errorf(`bits: unknown unit "%v" in value "%v"`, s[i], s) } s = s[:i] } else { return 0, fmt.Errorf(`bits: invalid value "%v"`, s) } } } d, err := strconv.ParseUint(s, 10, 64) if err != nil { return 0, fmt.Errorf(`bits: invalid value "%v"`, s) } // We do not control overflow on multiplication! return d * unit, nil } var byteUnitMap = map[byte]uint64{ 'K': 1 << 10, 'M': 1 << 20, 'G': 1 << 30, 'T': 1 << 40, 'P': 1 << 50, 'E': 1 << 60, } func ParseByteValues(s string) (uint64, error) { var unit uint64 = 1 // Special case: if all that is left is "0", this is zero. if s == "0" { return 0, nil } if s == "" { return 0, fmt.Errorf(`bytes: invalid value "%v"`, s) } // Cut 'Bps'. // if l := len(s); l > 4 && s[l-3:] == "Bps" { // s = s[:l-3] // } // Verifying the value. for i := 0; i < len(s); i++ { if s[i] < '0' || s[i] > '9' { // The last symbol. if i == len(s)-1 { var ok bool unit, ok = byteUnitMap[s[i]] if !ok { return 0, fmt.Errorf(`bytes: unknown unit "%v" in value "%v"`, s[i], s) } s = s[:i] } else { return 0, fmt.Errorf(`bytes: invalid value "%v"`, s) } } } d, err := strconv.ParseUint(s, 10, 64) if err != nil { return 0, fmt.Errorf(`bytes: invalid value "%v"`, s) } // We do not control overflow on multiplication! return d * unit, nil } ================================================ FILE: routeros/parse_data_information_units_test.go ================================================ package routeros import "testing" func TestParseBitValue(t *testing.T) { tests := []struct { name string arg string want uint64 wantErr bool }{ {"Positive #1", "30M", 30000000, false}, {"Positive #2", "30", 30, false}, {"Positive #3", "3E", 3000000000000000000, false}, {"Positive #4", "10Mbps", 10000000, false}, {"Negative #1", "30A", 0, true}, {"Negative #2", "30.0", 0, true}, {"Negative #3", "30Mb", 0, true}, {"Negative #4", "30m", 0, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := ParseBitValues(tt.arg) if (err != nil) != tt.wantErr { t.Errorf("ParseBitValue() error = %v, wantErr %v", err, tt.wantErr) return } if got != tt.want { t.Errorf("ParseBitValue() = %v, want %v", got, tt.want) } }) } } func TestParseByteValue(t *testing.T) { tests := []struct { name string arg string want uint64 wantErr bool }{ {"Positive #1", "64M", 67108864, false}, {"Positive #2", "30", 30, false}, {"Positive #3", "3E", 3458764513820540928, false}, {"Negative #1", "30A", 0, true}, {"Negative #2", "30.0", 0, true}, {"Negative #3", "30Mb", 0, true}, {"Negative #4", "30m", 0, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := ParseByteValues(tt.arg) if (err != nil) != tt.wantErr { t.Errorf("ParseByteValue() error = %v, wantErr %v", err, tt.wantErr) return } if got != tt.want { t.Errorf("ParseByteValue() = %v, want %v", got, tt.want) } }) } } ================================================ FILE: routeros/parse_duration.go ================================================ package routeros import ( "errors" "fmt" "strings" "time" ) // https://github.com/xhit/go-str2duration var unitMap = map[string]int64{ "us": int64(time.Nanosecond), "ms": int64(time.Millisecond), "s": int64(time.Second), "m": int64(time.Minute), "h": int64(time.Hour), "d": int64(time.Hour) * 24, "w": int64(time.Hour) * 168, } func ParseDuration(s string, baseUnits time.Duration) (time.Duration, error) { // ([0-9]*([0-9]*)?[a-z]+)+ orig := s var d int64 // 1d 00:00:00 if ss := strings.SplitN(s, " ", 2); len(ss) > 1 { res, err := ParseDuration(ss[1], baseUnits) if err != nil { return 0, err } d += int64(res) s = ss[0] } // Special case: if all that is left is "0", this is zero. if s == "0" { return 0, nil } if s == "" { return 0, fmt.Errorf(`time: invalid duration "%v"`, orig) } // hh:mm:ss format if ss := strings.Split(s, ":"); len(ss) == 3 { s = ss[0] + "h" + ss[1] + "m" + ss[2] } for s != "" { var ( v, f int64 // integers before, after decimal point scale float64 = 1 // value = v + f/scale ) var err error // The next character must be [0-9] if !(s[0] == '.' || '0' <= s[0] && s[0] <= '9') { return 0, fmt.Errorf(`time: invalid duration "%v"`, orig) } // Consume [0-9]* pl := len(s) v, s, err = leadingInt(s) if err != nil { return 0, fmt.Errorf(`time: invalid duration "%v"`, orig) } pre := pl != len(s) // whether we consumed anything before a period // Consume (\.[0-9]*)? post := false if s != "" && s[0] == '.' { s = s[1:] pl := len(s) f, scale, s = leadingFraction(s) post = pl != len(s) } if !pre && !post { // no digits (e.g. ".s" or "-.s") return 0, errors.New("time: invalid duration " + quote(orig)) } // Consume unit. i := 0 for ; i < len(s); i++ { c := s[i] if '0' <= c && c <= '9' { break } } var unit int64 if i != 0 { u := s[:i] s = s[i:] var ok bool unit, ok = unitMap[u] if !ok { return 0, fmt.Errorf(`time: unknown unit "%v" in duration "%v"`, u, orig) } } else { // missing unit in duration unit = int64(baseUnits) } if v > (1<<63-1)/unit { // overflow return 0, fmt.Errorf(`time: invalid duration "%v"`, orig) } v *= unit if f > 0 { // float64 is needed to be nanosecond accurate for fractions of hours. // v >= 0 && (f*unit/scale) <= 3.6e+12 (ns/h, h is the largest unit) v += int64(float64(f) * (float64(unit) / scale)) if v < 0 { // overflow return 0, fmt.Errorf(`time: invalid duration "%v"`, orig) } } d += v if d < 0 { // overflow return 0, fmt.Errorf(`time: invalid duration "%v"`, orig) } } return time.Duration(d), nil } func quote(s string) string { return "\"" + s + "\"" } var errLeadingInt = errors.New("time: bad [0-9]*") // never printed // leadingInt consumes the leading [0-9]* from s. func leadingInt(s string) (x int64, rem string, err error) { i := 0 for ; i < len(s); i++ { c := s[i] if c < '0' || c > '9' { break } if x > (1<<63-1)/10 { // overflow return 0, "", errLeadingInt } x = x*10 + int64(c) - '0' if x < 0 { // overflow return 0, "", errLeadingInt } } return x, s[i:], nil } // leadingFraction consumes the leading [0-9]* from s. // It is used only for fractions, so does not return an error on overflow, // it just stops accumulating precision. func leadingFraction(s string) (x int64, scale float64, rem string) { i := 0 scale = 1 overflow := false for ; i < len(s); i++ { c := s[i] if c < '0' || c > '9' { break } if overflow { continue } if x > (1<<63-1)/10 { // It's possible for overflow to give a positive number, so take care. overflow = true continue } y := x*10 + int64(c) - '0' if y < 0 { overflow = true continue } x = y scale *= 10 } return x, scale, s[i:] } ================================================ FILE: routeros/parse_duration_test.go ================================================ package routeros import ( "testing" "time" ) func TestParseDuration(t *testing.T) { type args struct { s string } tests := []struct { name string args args want time.Duration wantErr bool }{ {name: "300ms", args: args{"300ms"}, want: time.Duration(300 * time.Millisecond), wantErr: false}, {name: "300", args: args{"300"}, want: time.Duration(300 * time.Second), wantErr: false}, {name: "300s", args: args{"300s"}, want: time.Duration(300 * time.Second), wantErr: false}, {name: "00:00:10", args: args{"00:00:10"}, want: time.Duration(10 * time.Second), wantErr: false}, {name: "2h45m", args: args{"2h45m"}, want: time.Duration(2*time.Hour + 45*time.Minute), wantErr: false}, {name: "163w4d9h", args: args{"163w4d9h"}, want: time.Duration(98960400 * time.Second), wantErr: false}, {name: "27489h", args: args{"27489h"}, want: time.Duration(98960400 * time.Second), wantErr: false}, {name: "120ms", args: args{"0.12"}, want: time.Duration(120 * time.Millisecond), wantErr: false}, {name: "1d 00:00:00", args: args{"1d 00:00:00"}, want: time.Duration(24 * time.Hour), wantErr: false}, {name: "2w 1d 00:00:00", args: args{"2w 1d 00:00:00"}, want: time.Duration(24 * 15 * time.Hour), wantErr: false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := ParseDuration(tt.args.s, time.Second) if (err != nil) != tt.wantErr { t.Errorf("ParseDuration() error = %v, wantErr %v", err, tt.wantErr) return } if got != tt.want { t.Errorf("ParseDuration() got = %v, want %v", got, tt.want) } }) } } ================================================ FILE: routeros/parse_iprange.go ================================================ package routeros import ( "encoding/binary" "errors" "fmt" "strconv" "strings" ) // https://gist.github.com/vaerh/65b30353239cd17ee14f2e68983fbbf0 var pow = []int{24, 16, 8, 0} func ipToULong(ip string) (res uint32) { var oct uint64 for i, v := range strings.SplitN(ip, ".", 4) { oct, _ = strconv.ParseUint(v, 10, 32) res |= uint32(oct << pow[i]) } return } func IpRangeToCIDR(ip1, ip2 string) (string, error) { a1 := ipToULong(ip1) a2 := ipToULong(ip2) if a1 > a2 { return "", errors.New("start must be less than end") } var m uint32 = 0xFFFFFFFF l := 32 for m > 0 { m1 := m << 1 if (a1&m1) != a1 || (a1|^m1) > a2 { break } m = m1 l-- } var addr = make([]byte, 4) binary.BigEndian.PutUint32(addr, a1) a1 |= ^m if a2 < a1+1 { return fmt.Sprintf("%d.%d.%d.%d/%v", addr[0], addr[1], addr[2], addr[3], l), nil } return ip1 + "-" + ip2, nil } ================================================ FILE: routeros/parse_iprange_test.go ================================================ package routeros import "testing" func TestIpRangeToCIDR(t *testing.T) { type args struct { ip1 string ip2 string } tests := []struct { name string args args want string wantErr bool }{ {"Solid range", args{"192.168.0.0", "192.168.1.255"}, "192.168.0.0/23", false}, {"Non-solid range", args{"192.168.0.0", "192.168.1.254"}, "192.168.0.0-192.168.1.254", false}, {"Wrong range", args{"192.168.2.0", "192.168.1.255"}, "", true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := IpRangeToCIDR(tt.args.ip1, tt.args.ip2) if (err != nil) != tt.wantErr { t.Errorf("IpRangeToCIDR() error = %v, wantErr %v", err, tt.wantErr) return } if got != tt.want { t.Errorf("IpRangeToCIDR() got = %v, want %v", got, tt.want) } }) } } ================================================ FILE: routeros/provider.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) var ( ErrorMsgPut = "An error was encountered while sending a PUT request to the API: %v" ErrorMsgGet = "An error was encountered while sending a GET request to the API: %v" ErrorMsgPatch = "An error was encountered while sending a PATCH request to the API: %v" ErrorMsgDelete = "An error was encountered while sending a DELETE request to the API: %v" RouterOSVersion string ) // Generate the resources drift: // //go:generate go run ../tools/drift/main.go func Provider() *schema.Provider { return &schema.Provider{ Schema: map[string]*schema.Schema{ "hosturl": { Type: schema.TypeString, Required: true, DefaultFunc: schema.MultiEnvDefaultFunc( []string{"ROS_HOSTURL", "MIKROTIK_HOST"}, nil, ), Description: `URL of the MikroTik router, default is TLS connection to REST. * API: api[s]://host[:port] * api://router.local * apis://router.local:8729 * REST: http[s]://host * http://router.local * https://router.local * router.local * 127.0.0.1 export ROS_HOSTURL=router.local or export MIKROTIK_HOST=router.local `, }, "username": { Type: schema.TypeString, Required: true, DefaultFunc: schema.MultiEnvDefaultFunc( []string{"ROS_USERNAME", "MIKROTIK_USER"}, nil, ), Description: `Username for the MikroTik WEB/Winbox. export ROS_USERNAME=admin or export MIKROTIK_USER=admin `, }, "password": { Type: schema.TypeString, Optional: true, DefaultFunc: schema.MultiEnvDefaultFunc( []string{"ROS_PASSWORD", "MIKROTIK_PASSWORD"}, nil, ), Description: "Password for the MikroTik user (env: ROS_PASSWORD | MIKROTIK_PASSWORD).", Sensitive: true, }, "ca_certificate": { Type: schema.TypeString, Optional: true, DefaultFunc: schema.MultiEnvDefaultFunc( []string{"ROS_CA_CERTIFICATE", "MIKROTIK_CA_CERTIFICATE"}, nil, ), Description: "Path to MikroTik's certificate authority file (env: ROS_CA_CERTIFICATE | MIKROTIK_CA_CERTIFICATE).", }, "insecure": { Type: schema.TypeBool, Optional: true, DefaultFunc: schema.MultiEnvDefaultFunc( []string{"ROS_INSECURE", "MIKROTIK_INSECURE"}, false, ), Description: "Whether to verify the SSL certificate or not (env: ROS_INSECURE | MIKROTIK_INSECURE).", }, "suppress_syso_del_warn": { Type: schema.TypeBool, Optional: true, DefaultFunc: schema.MultiEnvDefaultFunc( []string{"ROS_SUPPRESS_SYSO_DEL_WARN"}, false, ), Description: "Suppress the system object deletion warning (env: ROS_SUPPRESS_SYSO_DEL_WARN).", }, "routeros_version": { Type: schema.TypeString, Optional: true, DefaultFunc: schema.MultiEnvDefaultFunc( []string{"ROS_VERSION"}, nil, ), Description: "RouterOS version for which resource schemes will be adapted. The version obtained from " + "MikroTik will be used if not specified (env: ROS_VERSION).", }, "rest_timeout": { Type: schema.TypeInt, Optional: true, Default: 59, Description: "HTTP Client Timeout", ValidateFunc: validation.IntAtLeast(5), }, }, ResourcesMap: map[string]*schema.Resource{ // IP objects "routeros_ip_address": ResourceIPAddress(), "routeros_ip_dhcp_client": ResourceDhcpClient(), "routeros_ip_dhcp_client_option": ResourceDhcpClientOption(), "routeros_ip_dhcp_relay": ResourceDhcpRelay(), "routeros_ip_dhcp_server": ResourceDhcpServer(), "routeros_ip_dhcp_server_config": ResourceDhcpServerConfig(), "routeros_ip_dhcp_server_network": ResourceDhcpServerNetwork(), "routeros_ip_dhcp_server_lease": ResourceDhcpServerLease(), "routeros_ip_dhcp_server_option": ResourceDhcpServerOption(), "routeros_ip_dhcp_server_option_matcher": ResourceDhcpServerOptionMatcher(), "routeros_ip_dhcp_server_option_sets": ResourceDhcpServerOptionSets(), "routeros_ip_dns": ResourceDns(), "routeros_ip_dns_adlist": ResourceDnsAdlist(), "routeros_ip_dns_forwarders": ResourceIpDnsForwarders(), "routeros_ip_dns_record": ResourceDnsRecord(), "routeros_ip_firewall_addr_list": ResourceIPFirewallAddrList(), "routeros_ip_firewall_connection_tracking": ResourceIPConnectionTracking(), "routeros_ip_firewall_filter": ResourceIPFirewallFilter(), "routeros_ip_firewall_layer7_protocol": ResourceIpFirewallLayer7Protocol(), "routeros_ip_firewall_mangle": ResourceIPFirewallMangle(), "routeros_ip_firewall_nat": ResourceIPFirewallNat(), "routeros_ip_firewall_raw": ResourceIPFirewallRaw(), "routeros_ip_hotspot": ResourceIpHotspot(), "routeros_ip_hotspot_ip_binding": ResourceIpHotspotIpBinding(), "routeros_ip_hotspot_profile": ResourceIpHotspotProfile(), "routeros_ip_hotspot_service_port": ResourceIpHotspotServicePort(), "routeros_ip_hotspot_user": ResourceIpHotspotUser(), "routeros_ip_hotspot_user_profile": ResourceIpHotspotUserProfile(), "routeros_ip_hotspot_walled_garden": ResourceIpHotspotWalledGarden(), "routeros_ip_hotspot_walled_garden_ip": ResourceIpHotspotWalledGardenIp(), "routeros_ip_nat_pmp": ResourceNatPmpSettings(), "routeros_ip_nat_pmp_interfaces": ResourceNatPmpInterfaces(), "routeros_ip_neighbor_discovery_settings": ResourceIpNeighborDiscoverySettings(), "routeros_ip_pool": ResourceIPPool(), "routeros_ip_route": ResourceIPRoute(), "routeros_ip_service": ResourceIpService(), "routeros_ip_settings": ResourceIpSettings(), "routeros_ip_smb": ResourceIpSMB(), "routeros_ip_ssh_server": ResourceIpSSHServer(), "routeros_ip_tftp": ResourceIpTFTP(), "routeros_ip_tftp_settings": ResourceIpTFTPSettings(), "routeros_ip_upnp": ResourceUPNPSettings(), "routeros_ip_upnp_interfaces": ResourceUPNPInterfaces(), "routeros_ip_vrf": ResourceIPVrf(), "routeros_ipv6_address": ResourceIPv6Address(), "routeros_ipv6_dhcp_client": ResourceIPv6DhcpClient(), "routeros_ipv6_dhcp_client_option": ResourceIPv6DhcpClientOption(), "routeros_ipv6_dhcp_server": ResourceIpv6DhcpServer(), "routeros_ipv6_dhcp_server_option": ResourceIpv6DhcpServerOption(), "routeros_ipv6_dhcp_server_option_sets": ResourceIpv6DhcpServerOptionSets(), "routeros_ipv6_firewall_addr_list": ResourceIPv6FirewallAddrList(), "routeros_ipv6_firewall_filter": ResourceIPv6FirewallFilter(), "routeros_ipv6_firewall_nat": ResourceIPv6FirewallNat(), "routeros_ipv6_firewall_mangle": ResourceIPv6FirewallMangle(), "routeros_ipv6_neighbor_discovery": ResourceIPv6NeighborDiscovery(), "routeros_ipv6_nd_prefix": ResourceIpv6NdPrefix(), "routeros_ipv6_pool": ResourceIpv6Pool(), "routeros_ipv6_route": ResourceIPv6Route(), "routeros_ipv6_settings": ResourceIpv6Settings(), // Aliases for IP objects to retain compatibility between original and fork "routeros_dhcp_client": ResourceDhcpClient(), "routeros_dhcp_client_option": ResourceDhcpClientOption(), "routeros_dhcp_server": ResourceDhcpServer(), "routeros_dhcp_server_network": ResourceDhcpServerNetwork(), "routeros_dhcp_server_lease": ResourceDhcpServerLease(), "routeros_firewall_addr_list": ResourceIPFirewallAddrList(), "routeros_firewall_filter": ResourceIPFirewallFilter(), "routeros_firewall_mangle": ResourceIPFirewallMangle(), "routeros_firewall_nat": ResourceIPFirewallNat(), "routeros_dns": ResourceDns(), "routeros_dns_record": ResourceDnsRecord(), // Interface Objects "routeros_interface_6to4": ResourceInterface6to4(), "routeros_interface_bonding": ResourceInterfaceBonding(), "routeros_interface_bridge_filter": ResourceInterfaceBridgeFilter(), "routeros_interface_bridge_port": ResourceInterfaceBridgePort(), "routeros_interface_bridge_settings": ResourceInterfaceBridgeSettings(), "routeros_interface_bridge_vlan": ResourceInterfaceBridgeVlan(), "routeros_interface_bridge": ResourceInterfaceBridge(), "routeros_interface_detect_internet": ResourceInterfaceDetectInternet(), "routeros_interface_dot1x_client": ResourceInterfaceDot1xClient(), "routeros_interface_dot1x_server": ResourceInterfaceDot1xServer(), "routeros_interface_eoip": ResourceInterfaceEoip(), "routeros_interface_ethernet": ResourceInterfaceEthernet(), "routeros_interface_ethernet_switch": ResourceInterfaceEthernetSwitch(), "routeros_interface_ethernet_switch_host": ResourceInterfaceEthernetSwitchHost(), "routeros_interface_ethernet_switch_port": ResourceInterfaceEthernetSwitchPort(), "routeros_interface_ethernet_switch_port_isolation": ResourceInterfaceEthernetSwitchPortIsolation(), "routeros_interface_ethernet_switch_rule": ResourceInterfaceEthernetSwitchRule(), "routeros_interface_ethernet_switch_vlan": ResourceInterfaceEthernetSwitchVlan(), "routeros_interface_gre": ResourceInterfaceGre(), "routeros_interface_gre6": ResourceInterfaceGre6(), "routeros_interface_ipip": ResourceInterfaceIPIP(), "routeros_interface_list": ResourceInterfaceList(), "routeros_interface_list_member": ResourceInterfaceListMember(), "routeros_interface_lte": ResourceInterfaceLte(), "routeros_interface_lte_apn": ResourceInterfaceLteApn(), "routeros_interface_l2tp_client": ResourceInterfaceL2tpClient(), "routeros_interface_l2tp_server": ResourceInterfaceL2tpServer(), "routeros_interface_macvlan": ResourceInterfaceMacVlan(), "routeros_interface_sstp_client": ResourceInterfaceSSTPClient(), "routeros_interface_sstp_server": ResourceInterfaceSSTPServer(), "routeros_interface_ovpn_client": ResourceOpenVPNClient(), "routeros_interface_ovpn_server": ResourceInterfaceOpenVPNServer(), "routeros_interface_pppoe_client": ResourceInterfacePPPoEClient(), "routeros_interface_pppoe_server": ResourceInterfacePppoeServer(), "routeros_interface_veth": ResourceInterfaceVeth(), "routeros_interface_vlan": ResourceInterfaceVlan(), "routeros_interface_vrrp": ResourceInterfaceVrrp(), "routeros_interface_vxlan": ResourceInterfaceVxlan(), "routeros_interface_vxlan_vteps": ResourceInterfaceVxlanVteps(), "routeros_interface_wireguard": ResourceInterfaceWireguard(), "routeros_interface_wireguard_peer": ResourceInterfaceWireguardPeer(), "routeros_interface_wireless": ResourceInterfaceWireless(), "routeros_interface_wireless_access_list": ResourceInterfaceWirelessAccessList(), "routeros_interface_wireless_cap": ResourceInterfaceWirelessCap(), "routeros_interface_wireless_connect_list": ResourceInterfaceWirelessConnectList(), "routeros_interface_wireless_security_profiles": ResourceInterfaceWirelessSecurityProfiles(), "routeros_interface_w60g": ResourceInterfaceW60g(), "routeros_interface_w60g_station": ResourceInterfaceW60gStation(), // Aliases for interface objects to retain compatibility between original and fork "routeros_bridge": ResourceInterfaceBridge(), "routeros_bridge_mlag": ResourceInterfaceBridgeMlag(), "routeros_bridge_port": ResourceInterfaceBridgePort(), "routeros_bridge_vlan": ResourceInterfaceBridgeVlan(), "routeros_gre": ResourceInterfaceGre(), "routeros_ipip": ResourceInterfaceIPIP(), "routeros_vlan": ResourceInterfaceVlan(), "routeros_vrrp": ResourceInterfaceVrrp(), "routeros_wireguard": ResourceInterfaceWireguard(), "routeros_wireguard_peer": ResourceInterfaceWireguardPeer(), // Aliases for backward compatibility "routeros_ip_dhcp_server_option_set": ResourceDhcpServerOptionSets(), // System Objects "routeros_disk_settings": ResourceDiskSettings(), "routeros_ip_cloud": ResourceIpCloud(), "routeros_ip_cloud_advanced": ResourceIpCloudAdvanced(), "routeros_system_certificate": ResourceSystemCertificate(), "routeros_system_certificate_scep_server": ResourceCertificateScepServer(), "routeros_certificate_scep_server": ResourceCertificateScepServer(), "routeros_system_clock": ResourceSystemClock(), "routeros_system_identity": ResourceSystemIdentity(), "routeros_system_led": ResourceSystemLed(), "routeros_system_led_settings": ResourceSystemLedSettings(), "routeros_system_logging": ResourceSystemLogging(), "routeros_system_logging_action": ResourceSystemLoggingAction(), "routeros_system_note": ResourceSystemNote(), "routeros_system_ntp_client": ResourceSystemNtpClient(), "routeros_system_ntp_server": ResourceSystemNtpServer(), "routeros_system_routerboard_button_mode": ResourceSystemRouterboardButtonMode(), "routeros_system_routerboard_button_reset": ResourceSystemRouterboardButtonReset(), "routeros_system_routerboard_button_wps": ResourceSystemRouterboardButtonWps(), "routeros_system_routerboard_settings": ResourceSystemRouterboardSettings(), "routeros_system_routerboard_usb": ResourceSystemRouterboardUsb(), "routeros_system_scheduler": ResourceSystemScheduler(), "routeros_system_script": ResourceSystemScript(), "routeros_system_user": ResourceUser(), "routeros_system_user_sshkeys": ResourceUserSshKeys(), "routeros_system_user_aaa": ResourceUserAaa(), "routeros_system_user_group": ResourceUserGroup(), "routeros_system_user_settings": ResourceSystemUserSettings(), // Aliases for system objects to retain compatibility between original and fork "routeros_identity": ResourceSystemIdentity(), "routeros_scheduler": ResourceSystemScheduler(), // CAPsMAN Objects "routeros_capsman_aaa": ResourceCapsManAaa(), "routeros_capsman_access_list": ResourceCapsManAccessList(), "routeros_capsman_channel": ResourceCapsManChannel(), "routeros_capsman_configuration": ResourceCapsManConfiguration(), "routeros_capsman_datapath": ResourceCapsManDatapath(), "routeros_capsman_interface": ResourceCapsManInterface(), "routeros_capsman_manager": ResourceCapsManManager(), "routeros_capsman_manager_interface": ResourceCapsManManagerInterface(), "routeros_capsman_provisioning": ResourceCapsManProvisioning(), "routeros_capsman_rates": ResourceCapsManRates(), "routeros_capsman_security": ResourceCapsManSecurity(), // Container objects "routeros_container": ResourceContainer(), "routeros_container_config": ResourceContainerConfig(), "routeros_container_envs": ResourceContainerEnvs(), "routeros_container_mounts": ResourceContainerMounts(), // File objects "routeros_file": ResourceFile(), // Routing "routeros_routing_bfd_configuration": ResourceRoutingBfdConfiguration(), "routeros_routing_bgp_connection": ResourceRoutingBgpConnection(), "routeros_routing_bgp_evpn": ResourceRoutingBgpEvpn(), "routeros_routing_bgp_instance": ResourceRoutingBgpInstance(), "routeros_routing_bgp_template": ResourceRoutingBgpTemplate(), "routeros_routing_bgp_vpn": ResourceRoutingBgpVpn(), "routeros_routing_filter_rule": ResourceRoutingFilterRule(), "routeros_routing_id": ResourceRoutingId(), "routeros_routing_igmp_proxy_interface": ResourceRoutingIgmpProxyInterface(), "routeros_routing_table": ResourceRoutingTable(), "routeros_routing_rule": ResourceRoutingRule(), // OSPF "routeros_routing_ospf_instance": ResourceRoutingOspfInstance(), "routeros_routing_ospf_area": ResourceRoutingOspfArea(), "routeros_routing_ospf_area_range": ResourceRoutingOspfAreaRange(), "routeros_routing_ospf_interface_template": ResourceRoutingOspfInterfaceTemplate(), // VPN "routeros_ip_ipsec_identity": ResourceIpIpsecIdentity(), "routeros_ip_ipsec_key": ResourceIpIpsecKey(), "routeros_ip_ipsec_mode_config": ResourceIpIpsecModeConfig(), "routeros_ip_ipsec_peer": ResourceIpIpsecPeer(), "routeros_ip_ipsec_policy": ResourceIpIpsecPolicy(), "routeros_ip_ipsec_policy_group": ResourceIpIpsecPolicyGroup(), "routeros_ip_ipsec_profile": ResourceIpIpsecProfile(), "routeros_ip_ipsec_proposal": ResourceIpIpsecProposal(), "routeros_ip_ipsec_settings": ResourceIpIpsecSettings(), "routeros_ovpn_server": ResourceOpenVPNServer(), // PPP "routeros_ppp_aaa": ResourcePppAaa(), "routeros_ppp_profile": ResourcePPPProfile(), "routeros_ppp_secret": ResourcePPPSecret(), // RADIUS "routeros_radius": ResourceRadius(), "routeros_radius_incoming": ResourceRadiusIncoming(), // SNMP "routeros_snmp": ResourceSNMP(), "routeros_snmp_community": ResourceSNMPCommunity(), // Helpers "routeros_wireguard_keys": ResourceWireguardKeys(), "routeros_move_items": ResourceMoveItems(), // Tools "routeros_tool_bandwidth_server": ResourceToolBandwidthServer(), "routeros_tool_email": ResourceToolEmail(), "routeros_tool_graphing_interface": ResourceToolGraphingInterface(), "routeros_tool_graphing_queue": ResourceToolGraphingQueue(), "routeros_tool_graphing_resource": ResourceToolGraphingResource(), "routeros_tool_mac_server": ResourceToolMacServer(), "routeros_tool_mac_server_winbox": ResourceToolMacServerWinBox(), "routeros_tool_mac_server_ping": ResourceToolMacServerPing(), "routeros_tool_netwatch": ResourceToolNetwatch(), "routeros_tool_sniffer": ResourceToolSniffer(), "routeros_ip_traffic_flow": ResourceIpTrafficFlow(), "routeros_ip_traffic_flow_target": ResourceIpTrafficFlowTarget(), "routeros_ip_traffic_flow_ipfix": ResourceIpTrafficFlowIpfix(), // User Manager "routeros_user_manager_advanced": ResourceUserManagerAdvanced(), "routeros_user_manager_attribute": ResourceUserManagerAttribute(), "routeros_user_manager_database": ResourceUserManagerDatabase(), "routeros_user_manager_limitation": ResourceUserManagerLimitation(), "routeros_user_manager_profile": ResourceUserManagerProfile(), "routeros_user_manager_profile_limitation": ResourceUserManagerProfileLimitation(), "routeros_user_manager_router": ResourceUserManagerRouter(), "routeros_user_manager_settings": ResourceUserManagerSettings(), "routeros_user_manager_user": ResourceUserManagerUser(), "routeros_user_manager_user_group": ResourceUserManagerUserGroup(), "routeros_user_manager_user_profile": ResourceUserManagerUserProfile(), // WiFi "routeros_wifi": ResourceWifi(), "routeros_wifi_aaa": ResourceWifiAaa(), "routeros_wifi_access_list": ResourceWifiAccessList(), "routeros_wifi_cap": ResourceWifiCap(), "routeros_wifi_capsman": ResourceWifiCapsman(), "routeros_wifi_channel": ResourceWifiChannel(), "routeros_wifi_configuration": ResourceWifiConfiguration(), "routeros_wifi_datapath": ResourceWifiDatapath(), "routeros_wifi_interworking": ResourceWifiInterworking(), "routeros_wifi_provisioning": ResourceWifiProvisioning(), "routeros_wifi_security": ResourceWifiSecurity(), "routeros_wifi_security_multi_passphrase": ResourceWifiSecurityMultiPassphrase(), "routeros_wifi_steering": ResourceWifiSteering(), // ZeroTier "routeros_zerotier": ResourceZerotier(), "routeros_zerotier_controller": ResourceZerotierController(), "routeros_zerotier_interface": ResourceZerotierInterface(), // Queue "routeros_queue_simple": ResourceQueueSimple(), "routeros_queue_tree": ResourceQueueTree(), "routeros_queue_type": ResourceQueueType(), // Hardware devices "routeros_interface_ethernet_switch_crs": ResourceInterfaceEthernetSwitchCrs(), "routeros_interface_ethernet_switch_crs_vlan": ResourceInterfaceEthernetSwitchCrsVlan(), "routeros_interface_ethernet_switch_crs_egress_vlan_tag": ResourceInterfaceEthernetSwitchCrsEgressVlanTag(), "routeros_interface_ethernet_switch_crs_egress_vlan_translation": ResourceInterfaceEthernetSwitchCrsEgressVlanTranslation(), "routeros_interface_ethernet_switch_crs_ingress_vlan_translation": ResourceInterfaceEthernetSwitchCrsIngressVlanTranslation(), }, DataSourcesMap: map[string]*schema.Resource{ "routeros_files": DatasourceFiles(), "routeros_interfaces": DatasourceInterfaces(), "routeros_interface_bridge_filter": DatasourceInterfaceBridgeFilter(), "routeros_ip_addresses": DatasourceIPAddresses(), "routeros_ip_arp": DatasourceIpArp(), "routeros_ip_dhcp_server_leases": DatasourceIpDhcpServerLeases(), "routeros_ip_firewall": DatasourceIPFirewall(), "routeros_ip_routes": DatasourceIPRoutes(), "routeros_ip_services": DatasourceIPServices(), "routeros_ipv6_addresses": DatasourceIPv6Addresses(), "routeros_ipv6_firewall": DatasourceIPv6Firewall(), "routeros_system_resource": DatasourceSystemResource(), "routeros_system_routerboard": DatasourceSystemRouterboard(), "routeros_wifi_easy_connect": DatasourceWiFiEasyConnect(), "routeros_x509": DatasourceX509(), // Aliases for entries that have been renamed "routeros_firewall": DatasourceIPFirewall(), }, ConfigureContextFunc: NewClient, } } func NewProvider() *schema.Provider { return Provider() } ================================================ FILE: routeros/provider_resource_state_migration.go ================================================ package routeros import ( "context" "fmt" "reflect" "strings" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func stateMigrationNameToId(resourcePath string) schema.StateUpgradeFunc { return func(ctx context.Context, rawState map[string]interface{}, m interface{}) (map[string]interface{}, error) { ColorizedMessage(ctx, INFO, fmt.Sprintf("ID attribute before migration: %#v", rawState["id"])) if rawState["id"] != nil { res, err := ReadItems(&ItemId{Name, rawState["id"].(string)}, resourcePath, m.(Client)) if err != nil { return nil, err } // Resource not found. if len(*res) == 0 { rawState["id"] = "" ColorizedMessage(ctx, WARN, "No resource found, but the scheme has been updated.", map[string]interface{}{"path": resourcePath, "id": rawState["id"]}) return rawState, nil } rawState["id"] = (*res)[0].GetID(Id) } ColorizedMessage(ctx, INFO, fmt.Sprintf("ID attribute after migration: %#v", rawState["id"])) return rawState, nil } } func stateMigrationScalarToList(keys ...string) schema.StateUpgradeFunc { return func(ctx context.Context, rawState map[string]interface{}, m interface{}) (map[string]interface{}, error) { for _, key := range keys { if rawState[key] == nil { continue } value := reflect.ValueOf(rawState[key]) if value.IsZero() { rawState[key] = []interface{}{} } if reflect.ValueOf(value).Kind() == reflect.String { rawState[key] = strings.Split(rawState[key].(string), ",") } slice := reflect.MakeSlice(reflect.SliceOf(value.Type()), 0, 1) reflect.Append(slice, value) rawState[key] = slice.Interface() } return rawState, nil } } ================================================ FILE: routeros/provider_schema_helpers.go ================================================ package routeros import ( "fmt" "regexp" "slices" "strconv" "strings" "time" "github.com/hashicorp/go-cty/cty" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) // All metadata fields must be present in each resource schema, and the field type must be string. const ( MetaId = "___id___" MetaResourcePath = "___path___" MetaTransformSet = "___ts___" MetaSkipFields = "___skip___" MetaSetUnsetFields = "___unset___" MetaDropByValue = "___drop_val___" ) const ( KeyActualMtu = "actual_mtu" KeyAllowFastPath = "allow_fast_path" KeyArp = "arp" KeyArpTimeout = "arp_timeout" KeyClampTcpMss = "clamp_tcp_mss" KeyComment = "comment" KeyDefault = "default" KeyDynamic = "dynamic" KeyDefaultName = "default_name" KeyDisabled = "disabled" KeyDontFragment = "dont_fragment" KeyDscp = "dscp" KeyEnabled = "enabled" KeyFilter = "filter" KeyHwOffloaded = "hw_offloaded" KeyInactive = "inactive" KeyInterface = "interface" KeyInvalid = "invalid" KeyIpsecSecret = "ipsec_secret" KeyKeepalive = "keepalive" KeyL2Mtu = "l2mtu" KeyLocalAddress = "local_address" KeyLoopProtect = "loop_protect" KeyLoopProtectDisableTime = "loop_protect_disable_time" KeyLoopProtectSendInterval = "loop_protect_send_interval" KeyLoopProtectStatus = "loop_protect_status" KeyMacAddress = "mac_address" KeyMtu = "mtu" KeyName = "name" KeyPlaceBefore = "place_before" KeyRemoteAddress = "remote_address" KeyRunning = "running" KeyVrf = "vrf" KeyVlanId = "vlan_id" ) // PropResourcePath Resource path property. func PropResourcePath(p string) *schema.Schema { return &schema.Schema{ Type: schema.TypeString, Optional: true, Default: p, Description: "Resource path for CRUD operations. This is an internal service field, setting a value is not required.", DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { return true }, } } // PropId Resource ID property. func PropId(t IdType) *schema.Schema { return &schema.Schema{ Type: schema.TypeInt, Optional: true, Default: int(t), Description: "Resource ID type (.id / name). This is an internal service field, setting a value is not required.", DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { return true }, } } func toQuotedCommaSeparatedString(s ...string) string { builder := strings.Builder{} const singleQuote = `"` const commaSingleQuote = `,"` builder.WriteString(singleQuote + s[0] + singleQuote) for i := 1; i < len(s); i++ { builder.WriteString(commaSingleQuote + s[i] + singleQuote) } return builder.String() } func PropDropByValue(s ...string) *schema.Schema { return &schema.Schema{ Type: schema.TypeString, Optional: true, Default: toQuotedCommaSeparatedString(s...), Description: "A list of values when generated by RouterOs will be dropped, useful to default values as 'unspecified' for null", DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { return true }, } } // PropTransformSet List of []string{"TF : MT", "TF : MT", ...} string pairs. func PropTransformSet(s ...string) *schema.Schema { return &schema.Schema{ Type: schema.TypeString, Optional: true, Default: toQuotedCommaSeparatedString(s...), Description: "A set of transformations for field names. This is an internal service field, setting a value is not required.", DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { return true }, } } // PropSkipFields SnakeName notation func PropSkipFields(s ...string) *schema.Schema { return &schema.Schema{ Type: schema.TypeString, Optional: true, Default: toQuotedCommaSeparatedString(s...), Description: "A set of transformations for field names. This is an internal service field, setting a value is not required.", DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { return true }, } } // PropSetUnsetFields func PropSetUnsetFields(s ...string) *schema.Schema { return &schema.Schema{ Type: schema.TypeString, Optional: true, Default: toQuotedCommaSeparatedString(s...), Description: "A set of fields that require setting/unsetting. This is an internal service field, setting a value is not required.", DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { return true }, } } // PropName func PropName(description string) *schema.Schema { return &schema.Schema{ Type: schema.TypeString, Required: true, Description: description, } } func PropNameOptional(description string) *schema.Schema { return &schema.Schema{ Type: schema.TypeString, Optional: true, Description: description, DiffSuppressFunc: AlwaysPresentNotUserProvided, } } // PropEnabled func PropEnabled(description string) *schema.Schema { return &schema.Schema{ Type: schema.TypeBool, Optional: true, Description: description, DiffSuppressFunc: AlwaysPresentNotUserProvided, } } // PropMacAddress func PropMacAddressRw(description string, required bool) *schema.Schema { mac := &schema.Schema{ Type: schema.TypeString, Description: description, ValidateFunc: validation.IsMACAddress, DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { if old != "" && d.GetRawConfig().GetAttr(k).IsNull() { return true } return strings.EqualFold(old, new) }, } if required { mac.Required = true } else { mac.Optional = true } return mac } func PropDefaultNameRo(description string) *schema.Schema { return &schema.Schema{ Type: schema.TypeString, Computed: true, Description: description, } } func PropVlanIdRw(description string, required bool) *schema.Schema { var ds schema.SchemaDiffSuppressFunc if !required { ds = AlwaysPresentNotUserProvided } return &schema.Schema{ Type: schema.TypeInt, Optional: !required, Required: required, Description: description, ValidateFunc: validation.IntBetween(0, 4094), DiffSuppressFunc: ds, } } // Schema properties. var ( PropActualMtuRo = &schema.Schema{ Type: schema.TypeInt, Computed: true, } PropAllowFastPathRw = &schema.Schema{ Type: schema.TypeBool, Optional: true, // Must be present in the request so that the IPSEC PSK can be set correctly. Description: "Whether to allow FastPath processing. Must be disabled if IPsec tunneling is used.", DiffSuppressFunc: AlwaysPresentNotUserProvided, } PropArpRw = &schema.Schema{ Type: schema.TypeString, Optional: true, Description: "Address Resolution Protocol mode:\n * disabled - the interface will not use ARP\n * enabled - " + "the interface will use ARP\n * local-proxy-arp - the router performs proxy ARP on the interface and sends " + "replies to the same interface\n * proxy-arp - the router performs proxy ARP on the interface and sends " + "replies to other interfaces\n * reply-only - the interface will only reply to requests originated from " + "matching IP address/MAC address combinations which are entered as static entries in the ARP table. No " + "dynamic entries will be automatically stored in the ARP table. Therefore for communications to be " + "successful, a valid static entry must already exist.", ValidateFunc: validation.StringInSlice([]string{"disabled", "enabled", "local-proxy-arp", "proxy-arp", "reply-only"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, } PropArpTimeoutRw = &schema.Schema{ Type: schema.TypeString, Optional: true, Description: "ARP timeout is time how long ARP record is kept in ARP table after no packets are received " + "from IP. Value auto equals to the value of arp-timeout in IP/Settings, default is 30s. Can use postfix " + "`ms`, `s`, `m`, `h`, `d` for milliseconds, seconds, minutes, hours or days. If no postfix is set then seconds (s) is used.", ValidateFunc: validation.StringMatch(regexp.MustCompile(`^$|auto$|(\d+(ms|s|m|h|d)?)+$`), "expected arp_timout value to be 'auto' string or time value"), DiffSuppressFunc: AlwaysPresentNotUserProvided, } PropClampTcpMssRw = &schema.Schema{ Type: schema.TypeBool, Optional: true, Description: "Controls whether to change MSS size for received TCP SYN packets. When enabled, a " + "router will change the MSS size for received TCP SYN packets if the current MSS size exceeds the " + "tunnel interface MTU (taking into account the TCP/IP overhead). The received encapsulated packet " + "will still contain the original MSS, and only after decapsulation the MSS is changed.", DiffSuppressFunc: AlwaysPresentNotUserProvided, } PropCommentRw = &schema.Schema{ Type: schema.TypeString, Optional: true, } PropDisabledRw = &schema.Schema{ Type: schema.TypeBool, Optional: true, DiffSuppressFunc: AlwaysPresentNotUserProvided, } PropDefaultRo = &schema.Schema{ Type: schema.TypeBool, Computed: true, Description: "It's the default item.", } PropDontFragmentRw = &schema.Schema{ Type: schema.TypeString, Optional: true, ValidateFunc: validation.StringInSlice([]string{"inherit", "no"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, } PropDscpRw = &schema.Schema{ // dscp (inherit | integer [0-63]; Default: '') Type: schema.TypeString, Optional: true, Default: "inherit", ValidateDiagFunc: func(v interface{}, p cty.Path) (diags diag.Diagnostics) { value := v.(string) if value == "" || value == "inherit" { return } i, err := strconv.Atoi(value) if err != nil { diags = diag.Errorf( "expected dscp value (%s) to be empty string or 'inherit' or integer 0..63", value) return } if i < 0 || i > 63 { diags = diag.Errorf( "expected %s to be in the range 0 - 63, got %d", value, i) return } return }, Description: "Set dscp value in GRE header to a fixed value '0..63' or 'inherit' from dscp value taken " + "from tunnelled traffic.", } PropDynamicRo = &schema.Schema{ Type: schema.TypeBool, Computed: true, Description: "Configuration item created by software, not by management interface. It is not exported, " + "and cannot be directly modified.", } PropFilterRw = &schema.Schema{ Type: schema.TypeMap, Optional: true, Elem: schema.TypeString, Description: "Additional request filtering options.", ValidateDiagFunc: ValidationMapKeyNames, } PropHwOffloadedRo = &schema.Schema{ Type: schema.TypeBool, Computed: true, Description: "Indicates whether the route is eligible to be hardware offloaded on supported hardware.", } PropInactiveRo = &schema.Schema{ Type: schema.TypeBool, Computed: true, } PropInterfaceRw = &schema.Schema{ Type: schema.TypeString, Required: true, Description: "Name of the interface.", } PropInvalidRo = &schema.Schema{ Type: schema.TypeBool, Computed: true, } PropIpsecSecretRw = &schema.Schema{ Type: schema.TypeString, Optional: true, Sensitive: true, Description: "When secret is specified, router adds dynamic IPsec peer to remote-address with " + "pre-shared key and policy (by default phase2 uses sha1/aes128cbc).", DiffSuppressFunc: AlwaysPresentNotUserProvided, } PropKeepaliveRw = &schema.Schema{ Type: schema.TypeString, Optional: true, ValidateFunc: validation.StringMatch(regexp.MustCompile(`^(\d+[smhdw]?)+(,\d+)?$`), "value must be integer[/time],integer 0..4294967295 (https://help.mikrotik.com/docs/display/ROS/GRE)"), Description: "Tunnel keepalive parameter sets the time interval in which the tunnel running flag will " + "remain even if the remote end of tunnel goes down. If configured time,retries fail, interface " + "running flag is removed. Parameters are written in following format: " + "`KeepaliveInterval,KeepaliveRetries` where `KeepaliveInterval` is time interval and " + "`KeepaliveRetries` - number of retry attempts. `KeepaliveInterval` is integer 0..4294967295", DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { if old == new { return true } if AlwaysPresentNotUserProvided(k, old, new, d) { return true } if old == "" || new == "" { return false } o := strings.Split(old, ",") n := strings.Split(new, ",") if len(o) != 2 || len(n) != 2 { panic(fmt.Sprintf("[Keepalive] wrong keepalive format, old: '%v', new: '%v'", old, new)) } // Compare keepalive retries. if o[1] != n[1] { return false } // Compare keepalive intervals. oDuration, err := ParseDuration(o[0], time.Second) if err != nil { panic("[Keepalive] parse 'old' duration error: " + err.Error()) } nDuration, err := ParseDuration(n[0], time.Second) if err != nil { panic("[Keepalive] parse 'new' duration error: " + err.Error()) } return oDuration.Seconds() == nDuration.Seconds() }, } PropL2MtuRo = &schema.Schema{ Type: schema.TypeInt, Computed: true, Description: "Layer2 Maximum transmission unit. " + "[See](https://wiki.mikrotik.com/wiki/Maximum_Transmission_Unit_on_RouterBoards).", } PropL2MtuRw = &schema.Schema{ Type: schema.TypeInt, Optional: true, Description: "Layer2 Maximum transmission unit. " + "[See](https://wiki.mikrotik.com/wiki/Maximum_Transmission_Unit_on_RouterBoards).", ValidateFunc: validation.IntBetween(1, 65535), DiffSuppressFunc: AlwaysPresentNotUserProvided, } PropLocalAddressRw = &schema.Schema{ Type: schema.TypeString, Optional: true, Description: "Source address of the tunnel packets, local on the router.", ValidateFunc: validation.IsIPAddress, DiffSuppressFunc: AlwaysPresentNotUserProvided, } PropLoopProtectRw = &schema.Schema{ Type: schema.TypeString, Optional: true, ValidateFunc: validation.StringInSlice([]string{"default", "on", "off"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, } PropLoopProtectDisableTimeRw = &schema.Schema{ Type: schema.TypeString, Optional: true, ValidateFunc: ValidationTime, DiffSuppressFunc: TimeEqual, } PropLoopProtectSendIntervalRw = &schema.Schema{ Type: schema.TypeString, Optional: true, ValidateFunc: ValidationTime, DiffSuppressFunc: TimeEqual, } PropLoopProtectStatusRo = &schema.Schema{ Type: schema.TypeString, Computed: true, } PropMacAddressRo = &schema.Schema{ Type: schema.TypeString, Computed: true, Description: "Current mac address.", } // TODO: Replace in all possible resources with a property without 'ForceNew'. // https://github.com/orgs/terraform-routeros/discussions/192#discussioncomment-5929999 PropNameForceNewRw = &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: true, Description: `Changing the name of this resource will force it to be recreated. > The links of other configuration properties to this resource may be lost! > Changing the name of the resource outside of a Terraform will result in a loss of control integrity for that resource! `, } PropPlaceBefore = &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: true, Description: `Before which position the rule will be inserted. > Please check the effect of this option, as it does not work as you think! > Best way to use in conjunction with a data source. See [example](../data-sources/ip_firewall.md#example-usage). `, } PropRemoteAddressRw = &schema.Schema{ Type: schema.TypeString, Optional: true, Description: "IP address of the remote end of the tunnel.", ValidateFunc: validation.IsIPAddress, DiffSuppressFunc: AlwaysPresentNotUserProvided, } PropRunningRo = &schema.Schema{ Type: schema.TypeBool, Computed: true, } PropVrfRw = &schema.Schema{ Type: schema.TypeString, Optional: true, Description: "The VRF table this resource operates on.", DiffSuppressFunc: AlwaysPresentNotUserProvided, } ) // PropMtuRw MTU value can be integer or 'auto'. func PropMtuRw() *schema.Schema { return &schema.Schema{ Type: schema.TypeString, Computed: true, Optional: true, ValidateDiagFunc: func(i interface{}, path cty.Path) diag.Diagnostics { v := i.(string) if v == "auto" { return nil } mtu, err := strconv.ParseInt(v, 0, 64) if err != nil { return diag.Diagnostics{ { Severity: diag.Error, Summary: "Expected MTU value to be integer or 'auto'", }, } } if mtu < 0 || mtu > 65535 { return diag.Diagnostics{ { Severity: diag.Error, Summary: "Expected MTU value to be in the range (0 - 65535), got " + v, }, } } return nil }, Description: "Layer3 Maximum transmission unit ('auto', 0 .. 65535). Look for the exact minimum value in " + "the MikroTik documentation", } } // Unified information on attribute deprecation func DeprecatedInfo(ros string) string { return "Deprecated since ROS v" + ros } // Properties validation. var ( Validation64k = validation.IntBetween(0, 65535) ValidationTime = validation.StringMatch(regexp.MustCompile(`^(\d+([smhdw]|ms)?)+$`), "value should be an integer or a time interval: 0..4294967295 (seconds) or 500ms, 2d, 1w") // ValidationDurationAtLeast returns a SchemaValidateDiagFunc which tests if the provided value // is a valid duration expected by RouterOS and is at least minDuration long (inclusive) ValidationDurationAtLeast = func(minDuration time.Duration) schema.SchemaValidateDiagFunc { return func(i interface{}, p cty.Path) diag.Diagnostics { value, ok := i.(string) if !ok { return diag.Errorf("expected type to be string") } duration, err := ParseDuration(value, time.Second) if err != nil { return diag.FromErr(err) } if duration < minDuration { return diag.Errorf("duration must be greater than %v", minDuration) } return diag.Diagnostics{} } } // ValidationDurationBetween returns a SchemaValidateFunc which tests if the provided value // is a valid duration expected by RouterOS and is between minVal and maxVal (inclusive) ValidationDurationBetween = func(minVal, maxVal int) schema.SchemaValidateFunc { return func(i interface{}, k string) (warnings []string, errors []error) { value, ok := i.(string) if !ok { errors = append(errors, fmt.Errorf("expected type of %s to be string", k)) return warnings, errors } duration, err := ParseDuration(value, time.Second) if err != nil { errors = append(errors, fmt.Errorf("expected %q to be a valid time, got %q: %+v", k, i, err)) return warnings, errors } return validation.IntBetween(minVal, maxVal)(int(duration.Seconds()), k) } } ValidationAutoYesNo = validation.StringInSlice([]string{"auto", "yes", "no"}, false) ValidationIpAddress = validation.StringMatch( regexp.MustCompile(`^$|^!?(\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(/([0-9]|[0-9]|[1-2][0-9]|3[0-2]))?)$`), "Allowed addresses should be a CIDR IP address or an empty string", ) ValidationMacAddress = validation.StringMatch( regexp.MustCompile(`^!?\b(?:[0-9A-F]{2}\:){5}(?:[0-9A-F]{2})$`), "Allowed MAC addresses should be [!]AA:BB:CC:DD:EE:FF", ) ValidationMacAddressWithMask = validation.StringMatch( regexp.MustCompile(`^!?\b(?:[0-9A-F]{2}\:){5}(?:[0-9A-F]{2})\/\b(?:[0-9A-F]{2}\:){5}(?:[0-9A-F]{2})$`), "Allowed MAC addresses should be [!]AA:BB:CC:DD:EE:FF/FF:FF:FF:FF:FF:FF", ) // ValidationMultiValInSlice returns a SchemaValidateDiagFunc which works like the StringInSlice function, // but the provided value can be a single value or a comma-separated list of values. // The negative indication of the parameter is also supported by adding "!" before value if mikrotikNegative is true. ValidationMultiValInSlice = func(valid []string, ignoreCase, mikrotikNegative bool) schema.SchemaValidateDiagFunc { return func(i interface{}, path cty.Path) (diags diag.Diagnostics) { v, ok := i.(string) if !ok { diags = append(diags, diag.Diagnostic{ Severity: diag.Error, Summary: "Bad value type", Detail: fmt.Sprintf("Value should be a string: %v (type = %T)", v, v), }) return } var negative []string if mikrotikNegative { for _, str := range valid { negative = append(negative, "!"+str) } } for _, sValue := range strings.Split(v, ",") { ok := false sValue = strings.TrimSpace(sValue) for _, sValid := range append(negative, valid...) { if sValue == sValid || (ignoreCase && strings.EqualFold(sValue, sValid)) { ok = true break } } if !ok { diags = append(diags, diag.Diagnostic{ Severity: diag.Error, Summary: "Bad value", Detail: fmt.Sprintf("Unexpected value: %v", sValue), }) } } return } } ValidationValInSlice = func(valid []string, ignoreCase, mikrotikNegative bool) schema.SchemaValidateDiagFunc { return func(i interface{}, path cty.Path) (diags diag.Diagnostics) { v, ok := i.(string) if !ok { diags = append(diags, diag.Diagnostic{ Severity: diag.Error, Summary: "Bad value type", Detail: fmt.Sprintf("Value should be a string: %v (type = %T)", v, v), }) return } var negative []string if mikrotikNegative { for _, str := range valid { negative = append(negative, "!"+str) } } v = strings.TrimSpace(v) for _, str := range append(negative, valid...) { if v == str || (ignoreCase && strings.EqualFold(v, str)) { return } } diags = append(diags, diag.Diagnostic{ Severity: diag.Error, Summary: "Bad value", Detail: fmt.Sprintf("Unexpected value: %v", v), }) return } } reTerraformField = regexp.MustCompile("^[a-z0-9_]+$") // ValidationMapKeyNames, A function to check map names for compliance with the TF standard. // When copying keys from a real configuration it is easy to make a mistake and transfer a key // containing dashes instead of underscores. This validator is added to prevent such errors. ValidationMapKeyNames = func(v interface{}, path cty.Path) diag.Diagnostics { var diags diag.Diagnostics for key := range v.(map[string]interface{}) { if reTerraformField.MatchString(key) { continue } diags = append(diags, diag.Diagnostic{ Severity: diag.Error, Summary: "Invalid attribute name", Detail: fmt.Sprintf("%s: Attribute name may only contain lowercase alphanumeric characters & "+ "underscores.", key), AttributePath: append(path, cty.IndexStep{Key: cty.StringVal(key)}), }) } return diags } ) // Properties DiffSuppressFunc. var ( // Composite parameter splitting function. split = func(r rune) bool { return r == '/' || r == ',' } // A common function for comparing time when it is specified in seconds. TimeEqual = func(k, old, new string, d *schema.ResourceData) bool { return timeEqual(k, old, new, d, time.Second) } // Function for comparing time at a base value other than seconds. TimeEqualU = func(baseUnits time.Duration) schema.SchemaDiffSuppressFunc { return func(k, old, new string, d *schema.ResourceData) bool { return timeEqual(k, old, new, d, baseUnits) } } timeControlWords = []string{"immediately", "infinity", "none"} timeEqual = func(k, old, new string, d *schema.ResourceData, baseUnits time.Duration) bool { if old == "" { return false } if AlwaysPresentNotUserProvided(k, old, new, d) { return true } // #447 routeros_ip_dhcp_server_config.store_leases_disk == "immediately" // routeros_ipv6_nd_prefix.preferred_lifetime == "infinity" if slices.Contains(timeControlWords, old) || slices.Contains(timeControlWords, new) { return old == new } // Compare intervals: oldSet := strings.FieldsFunc(old, split) newSet := strings.FieldsFunc(new, split) if len(oldSet) != len(newSet) { return false } for i, _ := range oldSet { o, err := ParseDuration(oldSet[i], baseUnits) if err != nil { panic("[TimeEquall] parse 'old' duration error: " + err.Error()) } n, err := ParseDuration(newSet[i], baseUnits) if err != nil { panic("[TimeEquall] parse 'new' duration error: " + err.Error()) } if o != n { return false } } return true } HexEqual = func(k, old, new string, d *schema.ResourceData) bool { if old == "" { return false } if AlwaysPresentNotUserProvided(k, old, new, d) { return true } // Compare numbers: var iOld, iNew int64 var err error iOld, err = strconv.ParseInt(old, 0, 64) if err != nil { panic("[HexEqual] 'old' number parse error: " + err.Error()) } iNew, err = strconv.ParseInt(new, 0, 64) if err != nil { panic("[HexEqual] 'new' number parse error: " + err.Error()) } return iOld == iNew } // AlwaysPresentNotUserProvided is a SupressDiff function that prevents values not provided by users to get updated. // This is necessary in some system-wide fields that are present regardless if the users provides any values. // Prevents the need of hardcode values for default values, as those are harder to track over time/versions of // routeros AlwaysPresentNotUserProvided = func(k, old, new string, d *schema.ResourceData) bool { value := d.GetRawConfig() // For lists and sets, the key will look like `something.12345` or `something.#`. // But in the raw config it will be just `something`. loop: for _, key := range strings.Split(k, ".") { if key == "#" || key == "%" { break } switch { case value.Type().IsObjectType(): value = value.GetAttr(key) case value.Type().IsMapType(): value = value.Index(cty.StringVal(key)) // Lists and sets should not be walked down as they are always updated as a whole. default: break loop } if value.IsNull() { return true } } return false } MacAddressEqual = func(k, old, new string, d *schema.ResourceData) bool { return strings.EqualFold(old, new) } BitsEqual = func(k, old, new string, d *schema.ResourceData) bool { if old == "" { return false } if AlwaysPresentNotUserProvided(k, old, new, d) { return true } // Compare values 30M/30M <> 30000000/30000000 or 30M <> 30000000: oldSet := strings.FieldsFunc(old, split) newSet := strings.FieldsFunc(new, split) if len(oldSet) != len(newSet) { return false } for i, _ := range oldSet { o, err := ParseBitValues(oldSet[i]) if err != nil { panic("[BitsEqual] parse 'old' value error: " + err.Error()) } n, err := ParseBitValues(newSet[i]) if err != nil { panic("[BitsEqual] parse 'new' value error: " + err.Error()) } if o != n { return false } } return true } BytesEqual = func(k, old, new string, d *schema.ResourceData) bool { if old == "" { return false } if AlwaysPresentNotUserProvided(k, old, new, d) { return true } if slices.Contains([]string{"unlimited"}, old) || slices.Contains([]string{"unlimited"}, new) { return old == new } // Compare values 64M/64M <> 67108864/67108864 or 64M <> 67108864: oldSet := strings.FieldsFunc(old, split) newSet := strings.FieldsFunc(new, split) if len(oldSet) != len(newSet) { return false } for i, _ := range oldSet { o, err := ParseByteValues(oldSet[i]) if err != nil { panic("[BytesEqual] parse 'old' value error: " + err.Error()) } n, err := ParseByteValues(newSet[i]) if err != nil { panic("[BytesEqual] parse 'new' value error: " + err.Error()) } if o != n { return false } } return true } ) // ImplicitSingleHostCIDR is a DiffSuppressFunc function that prevents a configuration such as `192.168.1.2/32` // constantly planning a diff against a parameter read as `192.168.1.2` (i.e. without a prefix length) where // the ROS API omits it for single hosts. ImplicitSingleHostCIDR should only be used where ROS does otherwise // admit a prefix length, i.e. `192.168.1.2/24` would both write and read back as such for the same parameter. func ImplicitSingleHostCIDR4(k, old, new string, d *schema.ResourceData) bool { return new == old + "/32" } func ImplicitSingleHostCIDR6(k, old, new string, d *schema.ResourceData) bool { return new == old + "/128" } func buildReadFilter(m map[string]any) []string { var res []string for fieldName, fieldValue := range m { res = append(res, fmt.Sprintf("%v=%v", SnakeToKebab(fieldName), fieldValue)) } return res } // Diagnostics var DeleteSystemObject = []diag.Diagnostic{{ Severity: diag.Warning, Summary: "Delete operation on a system object.", Detail: "This resource contains system settings and cannot be deleted or reset. " + "This action will remove the object from the Terraform state. " + "See also: 'terraform state rm' https://developer.hashicorp.com/terraform/cli/commands/state/rm", }} ================================================ FILE: routeros/provider_schema_helpers_test.go ================================================ package routeros import ( "testing" "time" "github.com/hashicorp/go-cty/cty" ) func TestValidationDurationAtLeast(t *testing.T) { const minDuration = time.Minute cases := []struct { arg string hasError bool }{ {"1s", true}, {"59", true}, {"1m", false}, {"1m1s", false}, {"00:01:01", false}, {"", true}, {"invalid", true}, } validator := ValidationDurationAtLeast(minDuration) for _, c := range cases { result := validator(c.arg, *new(cty.Path)) hasError := result.HasError() if hasError != c.hasError { t.Errorf("ValidationDurationAtLeast(%v)(%q, ...).hasError() == %t, want %t. Diagnostics: %v.", minDuration, c.arg, hasError, c.hasError, result) } } } func TestValidationMultiValInSlice(t *testing.T) { type args struct { valid []string ignoreCase bool mikrotikNegative bool value string } tests := []struct { name string args args want int }{ { "Positive #1", args{[]string{"a", "b", "c", "d"}, false, false, "a, b, c"}, 0, }, { "Positive #2", args{[]string{"a", "b", "c", "d"}, false, false, "a,b"}, 0, }, { "Positive #3", args{[]string{"a", "b", "c", "d"}, false, false, "d"}, 0, }, { "Positive #4", args{[]string{"a", "b", "c", "d"}, false, true, "a,!d"}, 0, }, { "Negative #1", args{[]string{"a", "b", "c", "d"}, false, false, "a,e"}, 1, }, { "Negative #2", args{[]string{"a", "b", "c", "d"}, false, false, "a,e,f,g"}, 3, }, { "Negative #3", args{[]string{"a", "b", "c", "d"}, false, false, "a,b,,,"}, 3, }, { "Positive #4", args{[]string{"a", "b", "c", "d"}, false, false, "a,!d"}, 1, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { f := ValidationMultiValInSlice(tt.args.valid, tt.args.ignoreCase, tt.args.mikrotikNegative) if got := f(tt.args.value, *new(cty.Path)); len(got) != tt.want { t.Errorf("ValidationMultiValInSlice() diag length = %v, want %v, diags: %v", len(got), tt.want, got) } }) } } func TestValidationValInSlice(t *testing.T) { type args struct { valid []string ignoreCase bool mikrotikNegative bool value string } tests := []struct { name string args args want int }{ { "Positive #1", args{[]string{"a", "b", "c", "d"}, false, false, " a "}, 0, }, { "Positive #2", args{[]string{"a", "b", "c", "d"}, false, false, "b"}, 0, }, { "Positive #3", args{[]string{"a", "b", "c", "d"}, false, false, "d"}, 0, }, { "Positive #4", args{[]string{"a", "b", "c", "d"}, false, true, "!d"}, 0, }, { "Positive #5", args{[]string{"a", "b", "c", "d"}, true, true, "!D"}, 0, }, { "Negative #1", args{[]string{"a", "b", "c", "d"}, false, false, "e"}, 1, }, { "Negative #2", args{[]string{"a", "b", "c", "d"}, false, false, "!a"}, 1, }, { "Positive #3", args{[]string{"a", "b", "c", "d"}, false, false, "A"}, 1, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { f := ValidationValInSlice(tt.args.valid, tt.args.ignoreCase, tt.args.mikrotikNegative) if got := f(tt.args.value, *new(cty.Path)); len(got) != tt.want { t.Errorf("ValidationValInSlice() diag length = %v, want %v, diags: %v", len(got), tt.want, got) } }) } } func Test_toQuotedCommaSeparatedString(t *testing.T) { tests := []struct { name string args []string want string }{ { "Concatenates many", []string{"a", "b", "c"}, `"a","b","c"`, }, { "Can do oney", []string{"a"}, `"a"`, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := toQuotedCommaSeparatedString(tt.args...) if got != tt.want { t.Errorf("toQuotedCommaSeparatedString() got = %v, want %v", got, tt.want) } }) } } func TestImplicitSingleHostCIDR4(t *testing.T) { type args struct { old string new string } tests := []struct { name string args args want bool }{ { "Single host pseudo-match", args{"192.168.1.2", "192.168.1.2/32"}, true, // diff suppress }, { "Subnet match", args{"172.34.45.67/24", "172.34.45.67/24"}, false, // no diff suppress }, { "Single host mismatch", args{"192.168.1.2", "192.168.3.4/32"}, false, // no diff suppress }, { "Subnet mismatch", args{"172.34.45.67/24", "10.1.2.3/24"}, false, // no diff suppress }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := ImplicitSingleHostCIDR4("", tt.args.old, tt.args.new, nil); got != tt.want { t.Errorf("ImplicitSingleHostCIDR4() suppress diff = %v, want %v", got, tt.want) } }) } } ================================================ FILE: routeros/provider_test.go ================================================ package routeros import ( "fmt" "io" "net/http" "os" "regexp" "strings" "testing" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" ) var testAccProvider *schema.Provider var testAccProviderFactories map[string]func() (*schema.Provider, error) var testNames = []string{"API", "REST"} var reHost = regexp.MustCompile(`^(?:\S+://)?(\S+?)(?::\d+)*$`) var reVersion = regexp.MustCompile(`\d+`) var providerConfig = ` provider "routeros" { insecure = true } ` func init() { testAccProvider = Provider() testAccProviderFactories = map[string]func() (*schema.Provider, error){ "routeros": func() (*schema.Provider, error) { return testAccProvider, nil }, } } func testCheckMinVersion(t *testing.T, version string) bool { // version: 6.39.1 var current, min uint64 if RouterOSVersion == "" { RouterOSVersion = os.Getenv("ROS_VERSION") } current, err := parseRouterOSVersion(RouterOSVersion) if err != nil { t.Fatal(err) } min, err = parseRouterOSVersion(version) if err != nil { t.Fatal(err) } return current >= min } func testCheckMaxVersion(t *testing.T, version string) bool { // version: 6.39.1 var current, max uint64 if RouterOSVersion == "" { RouterOSVersion = os.Getenv("ROS_VERSION") } current, err := parseRouterOSVersion(RouterOSVersion) if err != nil { t.Fatal(err) } max, err = parseRouterOSVersion(version) if err != nil { t.Fatal(err) } return current <= max } func TestCheckMinVersion(t *testing.T) { originalVersion := RouterOSVersion defer func() { RouterOSVersion = originalVersion }() type args struct { current string min string } tests := []struct { name string args args want bool }{ {"Positive #1", args{"7", "6.2.53"}, true}, {"Positive #2", args{"7.1", "6.2.53"}, true}, {"Positive #3", args{"7.1.35", "6.2.53"}, true}, {"Positive #4", args{"7.1.35", "6.2"}, true}, {"Positive #5", args{"7.1.35", "6"}, true}, {"Positive #6", args{"7", "7"}, true}, {"Positive #7", args{"7.1", "7.1"}, true}, {"Positive #8", args{"7.1.53", "7.1.53"}, true}, {"Negative #1", args{"6", "7.1.35"}, false}, {"Negative #2", args{"6.2", "7.1.35"}, false}, {"Negative #3", args{"6.2.53", "7.1.35"}, false}, {"Negative #4", args{"6.2.53", "7.1"}, false}, {"Negative #5", args{"6.2.53", "7"}, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { RouterOSVersion = tt.args.current if got := testCheckMinVersion(t, tt.args.min); got != tt.want { t.Errorf("TestCheckMinVersion() diag got = %v, want = %v", got, tt.want) } }) } } func testSetTransportEnv(t *testing.T, testName string) { host := reHost.FindStringSubmatch(os.Getenv("ROS_HOSTURL")) switch { case strings.Contains(testName, "API"): if err := os.Setenv("ROS_HOSTURL", "apis://"+host[1]); err != nil { t.Error(err) } case strings.Contains(testName, "REST"): if err := os.Setenv("ROS_HOSTURL", "https://"+host[1]); err != nil { t.Error(err) } default: t.Fatal("Unsupported test name format. The test must have the suffix API or REST.") } } func testAccPreCheck(t *testing.T) { if os.Getenv("ROS_HOSTURL") == "" || os.Getenv("ROS_USERNAME") == "" { t.Fatal("Environment variables (ROS_HOSTURL & ROS_USERNAME) must be set for testing") } for _, v := range Provider().ResourcesMap { checkResourceSchema(v.Schema, t) } } func checkResourceSchema(s map[string]*schema.Schema, t *testing.T) { f, ok := s[MetaResourcePath] if !ok { t.Fatalf("the schema does not contain field '%v'", MetaResourcePath) } if f.Default.(string) == "" { t.Fatalf("the field '%v', contains no data", MetaResourcePath) } f, ok = s[MetaId] if !ok { t.Fatalf("the schema does not contain field '%v'", MetaId) } if f.Default.(int) < 1 { t.Fatalf("the field '%v' is not defined", MetaId) } } func testCheckResourceDestroy(resourcePath, resourceType string) resource.TestCheckFunc { return func(s *terraform.State) error { cApi, _ := testAccProvider.Meta().(*ApiClient) cRest, _ := testAccProvider.Meta().(*RestClient) var testTransport TransportType switch testAccProvider.Meta().(type) { case *ApiClient: testTransport = TransportAPI case *RestClient: testTransport = TransportREST default: panic("[testCheckResourceDestroy] wrong transport type") } for _, rs := range s.RootModule().Resources { if rs.Type != resourceType { continue } id := rs.Primary.ID idName := IdType(Provider().ResourcesMap[resourceType].Schema[MetaId].Default.(int)).String() switch testTransport { case TransportAPI: cmd := []string{resourcePath + "/print", "?" + idName + "=" + id} res, err := cApi.RunArgs(cmd) if err != nil { return nil } if len(res.Re) > 0 { return fmt.Errorf("resource %v %s has been found", resourceType, id) } case TransportREST: // Escaping spaces! req, err := http.NewRequest("GET", fmt.Sprintf("%s/rest%s?%s=%s", cRest.HostURL, resourcePath, idName, strings.Replace(id, " ", "%20", -1)), nil) if err != nil { return err } req.Header.Set("Content-Type", "application/json") req.SetBasicAuth(cRest.Username, cRest.Password) res, err := cRest.Do(req) if err != nil { return err } if res == nil { return fmt.Errorf("the response body is empty") } if buf, _ := io.ReadAll(res.Body); string(buf) != "[]" { return fmt.Errorf("resource %v %s has been found", resourceType, id) } } return nil } return nil } } // testCheckResourceExists queries the MikroTik API and retrieves the matching resource parameters func testCheckResourceExists(name string, resourcePath string, resource *MikrotikItem) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[name] if !ok { return fmt.Errorf("%s: resource not found in terraform state", name) } id := rs.Primary.ID if id == "" { return fmt.Errorf("%s: no id is set", name) } resourceType := strings.Split(name, ".")[0] resourceSchema, ok := testAccProvider.ResourcesMap[resourceType] if !ok { return fmt.Errorf("%s: schema for '%s' resource not found", name, resourceType) } idType := IdType(resourceSchema.Schema[MetaId].Default.(int)) client := testAccProvider.Meta().(Client) resources, err := ReadItems(&ItemId{Type: idType, Value: id}, resourcePath, client) if err != nil { return err } if len(*resources) == 0 { return fmt.Errorf("%s: resource not found", name) } if resource != nil { *resource = (*resources)[0] } return nil } } ================================================ FILE: routeros/resource_actions.go ================================================ package routeros import ( "context" "errors" "fmt" "strings" "time" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) type DataValidateFunc func(d *schema.ResourceData) diag.Diagnostics var errorNoLongerExists = errors.New("resource no longer exists") // Dynamic resource ID lookup to save us from situations where we are trying to delete a resource // that has been destroyed outside of Terraform. Always returns only the internal Mikrotik id! func dynamicIdLookup(idType IdType, path string, c Client, d *schema.ResourceData) (string, error) { // Dynamic lookup id. res, err := ReadItems(&ItemId{idType, d.Id()}, path, c) if err != nil { // API/REST client error. return "", err } // Resource not found. if len(*res) == 0 { d.SetId("") return "", errorNoLongerExists } return (*res)[0].GetID(Id), nil } // Passing the called CRUD method on creation through an existing context. type ctxCrudMethod string const ctxCrudMethodKey = "crudMethod" // Specifies a CRUD method as part of the resource schema description. func ctxSetCrudMethod(ctx context.Context, m crudMethod) context.Context { return context.WithValue(ctx, ctxCrudMethod(ctxCrudMethodKey), m) } // Retrieve a CRUD method as part of the processing of a resource creation request. func ctxGetCrudMethod(ctx context.Context) crudMethod { if v := ctx.Value(ctxCrudMethod(ctxCrudMethodKey)); v != nil { return v.(crudMethod) } return crudUnknown } // ResourceCreate - Creation of a resource in accordance with the TF Schema. // It is possible to transparently pass the request type (CRUD Method) within an existing context. // // CreateContext: func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { // return ResourceCreate(ctxSetCrudMethod(ctx, crudGenerateKey), resSchema, d, m) // }, func ResourceCreate(ctx context.Context, s map[string]*schema.Schema, d *schema.ResourceData, m interface{}) diag.Diagnostics { item, metadata := TerraformResourceDataToMikrotik(s, d) res, err := CreateItem(ctx, item, metadata.Path, m.(Client)) if err != nil { ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgPut, err)) return diag.FromErr(err) } // Some resources may return an empty array as a response when executing commands other than 'create'. // For these cases, we will try to find the created element by name (if available). if res.GetID(Id) == "" && item[KeyName] != "" { items, err := ReadItems(&ItemId{Name, item[KeyName]}, metadata.Path, m.(Client)) if err != nil { return diag.FromErr(err) } if items != nil && len(*items) == 1 { res = (*items)[0] } } // ... If no ID is set, Terraform assumes the resource was not created successfully; // as a result, no state will be saved for that resource. if res.GetID(Id) == "" { return diag.Diagnostics{ diag.Diagnostic{ Severity: diag.Error, Summary: "The resource ID was not found in the response", }, } } // At this time, we have a successfully created resource, // regardless of the success of its reading. switch metadata.IdType { case Id: // Response ID. d.SetId(res.GetID(Id)) case Name: // Resource ID. d.SetId(item.GetID(Name)) } // We ask for information again in the case of API. if m.(Client).GetTransport() == TransportAPI { r, err := ReadItems(&ItemId{Id, res.GetID(Id)}, metadata.Path, m.(Client)) if err != nil { ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgPut, err)) return diag.FromErr(err) } if len(*r) == 0 { return diag.Diagnostics{ diag.Diagnostic{ Severity: diag.Error, Summary: fmt.Sprintf("Mikrotik resource path='%v' id='%v' not found", metadata.Path, res.GetID(Id)), }, } } res = (*r)[0] } //spew.Dump(res) return MikrotikResourceDataToTerraform(res, s, d) } func ResourceCreateAndWait(ctx context.Context, s map[string]*schema.Schema, d *schema.ResourceData, m interface{}, timeout time.Duration) diag.Diagnostics { item, metadata := TerraformResourceDataToMikrotik(s, d) if item[KeyName] == "" { panic("Asynchronous resource creation should be applied to objects that have the 'name' attribute.") } ColorizedDebug(ctx, fmt.Sprintf("Wait timeout is %s", timeout)) // The lifetime of a REST session is 60 seconds. _, err := CreateItem(ctx, item, metadata.Path, m.(Client)) if err != nil { // context deadline exceeded (Client.Timeout exceeded while awaiting headers) // {"detail":"Session closed","error":400,"message":"Bad Request"} // from RouterOS device: action timed out - try again, if error continues contact MikroTik support and send a supout file (13) if !strings.Contains(err.Error(), context.DeadlineExceeded.Error()) && !strings.Contains(err.Error(), "Session closed") && !strings.Contains(err.Error(), "action timed out - try again") { ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgPut, err)) return diag.FromErr(err) } ColorizedDebug(ctx, "Timeout, the Create context is canceled, waiting for the resource to be created. "+ "Session termination by MikroTik is ignored.") } // context deadline exceeded var res MikrotikItem // During RSA key generation, we can get 100% CPU utilization of the MT. // During this time MT may stop accepting external requests! localCtx, cancel := context.WithTimeout(context.Background(), timeout) attempt := 0 for { defer cancel() // We will try to find the created element by name (if available). items, err := ReadItems(&ItemId{Name, item[KeyName]}, metadata.Path, m.(Client)) if err != nil { ColorizedMessage(ctx, TRACE, fmt.Sprintf("Timeout, the Read context is canceled, waiting for the resource to be created. "+ "Session termination by MikroTik is ignored. Attempt #%v", attempt)) attempt++ } if items != nil && len(*items) == 1 { res = (*items)[0] break } select { case <-localCtx.Done(): // The context deadline has been exceeded. return diag.Diagnostics{ diag.Diagnostic{ Severity: diag.Error, Summary: "The resource ID was not found in the response", }, } default: time.Sleep(15 * time.Second) } } // ... If no ID is set, Terraform assumes the resource was not created successfully; // as a result, no state will be saved for that resource. if res.GetID(Id) == "" { return diag.Diagnostics{ diag.Diagnostic{ Severity: diag.Error, Summary: "The resource ID was not found in the response", }, } } // At this time, we have a successfully created resource, // regardless of the success of its reading. switch metadata.IdType { case Id: // Response ID. d.SetId(res.GetID(Id)) case Name: // Resource ID. d.SetId(item.GetID(Name)) } // We ask for information again in the case of API. if m.(Client).GetTransport() == TransportAPI { r, err := ReadItems(&ItemId{Id, res.GetID(Id)}, metadata.Path, m.(Client)) if err != nil { ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgPut, err)) return diag.FromErr(err) } if len(*r) == 0 { return diag.Diagnostics{ diag.Diagnostic{ Severity: diag.Error, Summary: fmt.Sprintf("Mikrotik resource path='%v' id='%v' not found", metadata.Path, res.GetID(Id)), }, } } res = (*r)[0] } //spew.Dump(res) return MikrotikResourceDataToTerraform(res, s, d) } // ResourceRead Reading some information about one specific resource. func ResourceRead(ctx context.Context, s map[string]*schema.Schema, d *schema.ResourceData, m interface{}) diag.Diagnostics { metadata := GetMetadata(s) res, err := ReadItems(&ItemId{metadata.IdType, d.Id()}, metadata.Path, m.(Client)) if err != nil { ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgGet, err)) return diag.FromErr(err) } // Resource not found. if len(*res) == 0 { d.SetId("") return nil } d.SetId((*res)[0].GetID(metadata.IdType)) return MikrotikResourceDataToTerraform((*res)[0], s, d) } // ResourceUpdate Updating the resource in accordance with the TF Schema. func ResourceUpdate(ctx context.Context, s map[string]*schema.Schema, d *schema.ResourceData, m interface{}) diag.Diagnostics { item, metadata := TerraformResourceDataToMikrotik(s, d) // d.Id() can be the name of a resource or its identifier. // Mikrotik only operates on resource ID! id, err := dynamicIdLookup(metadata.IdType, metadata.Path, m.(Client), d) if err != nil { // There is nothing to update, because resource id not found // or some other error. ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgPatch, err)) return diag.FromErr(err) } res, err := UpdateItem(&ItemId{Id, id}, metadata.Path, item, m.(Client)) if err != nil { ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgPatch, err)) return diag.FromErr(err) } return MikrotikResourceDataToTerraform(res, s, d) } // ResourceDelete Deleting the resource. func ResourceDelete(ctx context.Context, s map[string]*schema.Schema, d *schema.ResourceData, m interface{}) diag.Diagnostics { metadata := GetMetadata(s) id, err := dynamicIdLookup(metadata.IdType, metadata.Path, m.(Client), d) if err != nil { if err != errorNoLongerExists { ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgDelete, err)) return diag.FromErr(err) } // We inform the user that the resource no longer exists. d.SetId("") return diag.Diagnostics{ diag.Diagnostic{ Severity: diag.Warning, Summary: errorNoLongerExists.Error(), }, } } if err := DeleteItem(&ItemId{Id, id}, metadata.Path, m.(Client)); err != nil { ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgDelete, err)) return diag.FromErr(err) } d.SetId("") return nil } // SystemResourceRead The difference from the normal reading is in the method of generation of Id. func SystemResourceRead(ctx context.Context, s map[string]*schema.Schema, d *schema.ResourceData, m interface{}) diag.Diagnostics { metadata := GetMetadata(s) res := MikrotikItem{} err := m.(Client).SendRequest(crudRead, &URL{Path: metadata.Path}, nil, &res) if err != nil { return diag.FromErr(err) } // We make a unique Id, it does not affect the work with the Mikrotik. // Id: /caps-man/manager -> caps-man.manager d.SetId(strings.ReplaceAll(strings.TrimLeft(metadata.Path, "/"), "/", ".")) return MikrotikResourceDataToTerraform(res, s, d) } // SystemResourceCreateUpdate A resource cannot be created, it can only be changed. func SystemResourceCreateUpdate(ctx context.Context, s map[string]*schema.Schema, d *schema.ResourceData, m interface{}) diag.Diagnostics { item, metadata := TerraformResourceDataToMikrotik(s, d) var resUrl string if m.(Client).GetTransport() == TransportREST { // https://router/rest/system/identity/set // https://router/rest/caps-man/manager/set resUrl = "/set" } err := m.(Client).SendRequest(crudPost, &URL{Path: metadata.Path + resUrl}, item, nil) if err != nil { return diag.FromErr(err) } return SystemResourceRead(ctx, s, d, m) } // SystemResourceDelete Delete function will remove the object from the Terraform state // No delete functionality provided by API for System Resources. func SystemResourceDelete(ctx context.Context, s map[string]*schema.Schema, d *schema.ResourceData, m interface{}) diag.Diagnostics { d.SetId("") if m.(Client).GetExtraParams().SuppressSysODelWarn { return nil } return DeleteSystemObject } // ImportStateCustomContext is an implementation of StateContextFunc that can be used to // import resources with the ability to explicitly or implicitly specify a key field. // `terraform [global options] import [options] ADDR ID`. // During import the content of the `ID` is checked and depending on the specified string it is possible to automatically search for the internal Mikrotik identifier. // Logic of `ID` processing // - The first character of the string contains an asterisk (standard Mikrotik identifier `*3E`): import without additional search. // - String containing no "=" character (`wifi-01`): the "name" field is used for searching. // - String containing only one "=" character (`"comment=hAP-ac3"`): the "word left" and "word right" pair is used for searching. func ImportStateCustomContext(s map[string]*schema.Schema) schema.StateContextFunc { return func(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { id := d.Id() fieldName := "name" if len(id) == 0 || id[0] == '*' { return []*schema.ResourceData{d}, nil } else { // By default, we filter by the "name" field if s := strings.Split(id, "="); len(s) == 2 { // field=value fieldName = s[0] id = s[1] } } path := s[MetaResourcePath].Default.(string) res, err := ReadItemsFiltered([]string{SnakeToKebab(fieldName) + "=" + id}, path, m.(Client)) if err != nil { return nil, err } switch len(*res) { case 0: return nil, fmt.Errorf("resource not found: %v=%v", fieldName, id) case 1: retId, ok := (*res)[0][Id.String()] if !ok { return nil, fmt.Errorf("attribute %v not found in the response", Id.String()) } d.SetId(retId) default: return nil, fmt.Errorf("more than one resource found: %v=%v", fieldName, id) } return []*schema.ResourceData{d}, nil } } ================================================ FILE: routeros/resource_actions_default.go ================================================ package routeros import ( "context" "fmt" "time" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func DefaultCreate(s map[string]*schema.Schema) schema.CreateContextFunc { return func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { return ResourceCreate(ctx, s, d, m) } } func DefaultCreateWithTimeout(s map[string]*schema.Schema, t time.Duration) schema.CreateContextFunc { return func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { return ResourceCreate(ctx, s, d, m) } } func DefaultValidateCreate(s map[string]*schema.Schema, f DataValidateFunc) schema.CreateContextFunc { return func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { if f != nil { if diags := f(d); diags.HasError() { return diags } } return ResourceCreate(ctx, s, d, m) } } func DefaultRead(s map[string]*schema.Schema) schema.ReadContextFunc { return func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { return ResourceRead(ctx, s, d, m) } } func DefaultUpdate(s map[string]*schema.Schema) schema.UpdateContextFunc { return func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { return ResourceUpdate(ctx, s, d, m) } } func DefaultValidateUpdate(s map[string]*schema.Schema, f DataValidateFunc) schema.UpdateContextFunc { return func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { if f != nil { if diags := f(d); diags.HasError() { return diags } } return ResourceUpdate(ctx, s, d, m) } } func DefaultDelete(s map[string]*schema.Schema) schema.DeleteContextFunc { return func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { return ResourceDelete(ctx, s, d, m) } } // Function to update resources that are present in the system by default out of the box. // The distinctive feature of such resources is that they cannot be deleted, but they can be modified. // For example, enabling/disabling the resource. // // FIXME Replace fucntions in resources: ResourceInterfaceEthernetSwitchPortIsolation, ResourceInterfaceEthernetSwitchPort // ResourceInterfaceEthernetSwitch, ResourceInterfaceLte, ResourceIpService func DefaultCreateUpdate(s map[string]*schema.Schema) func(context.Context, *schema.ResourceData, interface{}) diag.Diagnostics { return func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { item, metadata := TerraformResourceDataToMikrotik(s, d) res, err := ReadItems(&ItemId{Name, d.Get("name").(string)}, metadata.Path, m.(Client)) if err != nil { // API/REST client error. ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgPatch, err)) return diag.FromErr(err) } // Resource not found. if len(*res) == 0 { d.SetId("") ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgPatch, err)) return diag.FromErr(errorNoLongerExists) } d.SetId((*res)[0].GetID(Id)) item[".id"] = d.Id() var resUrl string if m.(Client).GetTransport() == TransportREST { resUrl = "/set" } err = m.(Client).SendRequest(crudPost, &URL{Path: metadata.Path + resUrl}, item, nil) if err != nil { return diag.FromErr(err) } return ResourceRead(ctx, s, d, m) } } ================================================ FILE: routeros/resource_actions_default_system.go ================================================ package routeros import ( "context" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func DefaultSystemCreate(s map[string]*schema.Schema) schema.CreateContextFunc { return func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { return SystemResourceCreateUpdate(ctx, s, d, m) } } func DefaultSystemRead(s map[string]*schema.Schema) schema.ReadContextFunc { return func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { return SystemResourceRead(ctx, s, d, m) } } func DefaultSystemUpdate(s map[string]*schema.Schema) schema.UpdateContextFunc { return func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { return SystemResourceCreateUpdate(ctx, s, d, m) } } func DefaultSystemDelete(s map[string]*schema.Schema) schema.DeleteContextFunc { return func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { return SystemResourceDelete(ctx, s, d, m) } } func DefaultSystemDatasourceRead(s map[string]*schema.Schema) schema.ReadContextFunc { return func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { res := MikrotikItem{} path := s[MetaResourcePath].Default.(string) err := m.(Client).SendRequest(crudRead, &URL{Path: path}, nil, &res) if err != nil { return diag.FromErr(err) } return MikrotikResourceDataToTerraformDatasource(&[]MikrotikItem{res}, "", s, d) } } ================================================ FILE: routeros/resource_actions_test.go ================================================ package routeros import ( "encoding/json" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "log" "reflect" "strconv" "testing" ) func Test_defaultResourceCreateContext(t *testing.T) { //a:= DefaultCreate[client.GRE]() rs := ResourceInterfaceGre() rd := rs.TestResourceData() m := map[string]string{} for k, v := range rs.Schema { if v.Computed { continue } // TODO Check this! if !rd.HasChange(k) && v.Optional { continue } switch reflect.TypeOf(rd.Get(k)).Kind() { case reflect.String: m[SnakeToKebab(k)] = rd.Get(k).(string) case reflect.Int: m[SnakeToKebab(k)] = strconv.Itoa(rd.Get(k).(int)) case reflect.Bool: m[SnakeToKebab(k)] = BoolToMikrotikJSON(rd.Get(k).(bool)) } } b, _ := json.Marshal(m) log.Println(string(b)) s := `{".id":"*39","allow-fast-path":"true","clamp-tcp-mss":"true","comment":"New comment","disabled":"true",` + `"dont-fragment":"","dscp":"","ipsec-secret":"12321","keepalive":"","local-address":"","mtu":"1472",` + `"name":"gre","remote-address":""}` m2 := map[string]string{} err := json.Unmarshal([]byte(s), &m2) if err != nil { t.Error(err.Error()) } for k, v := range m2 { if KebabToSnake(k) == ".id" { rd.SetId(v) continue } if _, ok := rs.Schema[KebabToSnake(k)]; !ok { t.Errorf("Lost field: %v", KebabToSnake(k)) } log.Println(KebabToSnake(k)) switch rs.Schema[KebabToSnake(k)].Type { case schema.TypeString: _ = rd.Set(KebabToSnake(k), v) case schema.TypeInt: i, _ := strconv.Atoi(v) _ = rd.Set(KebabToSnake(k), i) case schema.TypeBool: _ = rd.Set(KebabToSnake(k), BoolFromMikrotikJSON(v)) default: t.Error(rs.Schema[KebabToSnake(k)].Type) } } } ================================================ FILE: routeros/resource_capsman_access_list.go ================================================ package routeros import ( "context" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "allow-signal-out-of-range": "10s", "comment": "Laptop", "disabled": "false", "mac-address": "00:00:00:00:00:00", "signal-range": "-120..120", "ssid-regexp": "", "time": "0s-1d,sun,mon,tue,wed,thu,fri,sat", "vlan-id": "1", "vlan-mode": "use-tag" } */ // https://help.mikrotik.com/docs/display/ROS/CAPsMAN func ResourceCapsManAccessList() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/caps-man/access-list"), MetaId: PropId(Id), KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "action": { Type: schema.TypeString, Optional: true, Description: "An action to take when a client matches.", ValidateFunc: validation.StringInSlice([]string{"accept", "reject", "query-radius"}, false), }, "allow_signal_out_of_range": { Type: schema.TypeString, Optional: true, Default: "10s", Description: "An option that permits the client's signal to be out of the range always or for some time interval.", DiffSuppressFunc: TimeEqual, }, "ap_tx_limit": { Type: schema.TypeInt, Optional: true, Description: "Transmission speed limit in the direction of the client..", }, "client_to_client_forwarding": { Type: schema.TypeBool, Optional: true, Description: "An option that specifies whether to allow forwarding data between clients connected to the same interface.", }, "client_tx_limit": { Type: schema.TypeInt, Optional: true, Description: "Transmission speed limit in the direction of the access point.", }, "mac_address": { Type: schema.TypeString, Optional: true, Description: "MAC address of the client.", }, "mac_mask": { Type: schema.TypeString, Optional: true, Description: "MAC address mask to apply when comparing clients' addresses.", }, "interface": { Type: schema.TypeString, Optional: true, Description: "Interface name to compare with an interface to which the client actually connects to.", }, KeyPlaceBefore: PropPlaceBefore, "private_passphrase": { Type: schema.TypeString, Optional: true, Description: "PSK passphrase for the client if some PSK authentication algorithm is used.", }, "radius_accounting": { Type: schema.TypeBool, Optional: true, Description: "An option that specifies if RADIUS traffic accounting should be used in case of RADIUS authentication of the client.", }, "signal_range": { Type: schema.TypeString, Optional: true, Default: "-120..120", Description: "The range in which the client signal must fall.", }, "ssid_regexp": { Type: schema.TypeString, Optional: true, Description: "The regular expression to compare the actual SSID the client connects to.", }, "time": { Type: schema.TypeString, Optional: true, Default: "0s-1d,sun,mon,tue,wed,thu,fri,sat", Description: "Time of the day and days of the week when the rule is applicable.", }, KeyVlanId: PropVlanIdRw("VLAN ID to use if vlan-mode enables use of VLAN tagging.", false), "vlan_mode": { Type: schema.TypeString, Optional: true, Description: "VLAN tagging mode specifies if traffic coming from a client should get tagged and untagged when it goes back to the client.", ValidateFunc: validation.StringInSlice([]string{"no-tag", "use-service-tag", "use-tag"}, false), }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { resSchema[MetaSkipFields].Default = `"place_before"` defer func() { resSchema[MetaSkipFields].Default = `` }() return ResourceUpdate(ctx, resSchema, d, m) }, DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_capsman_channel.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "band": "2ghz-b", "control-channel-width": "5mhz", "extension-channel": "disabled", "frequency": "2112", "name": "channel1", "reselect-interval": "10m", "save-selected": "true", "secondary-frequency": "disabled", "skip-dfs-channels": "true", "tx-power": "-20" } */ // https://help.mikrotik.com/docs/display/ROS/CAPsMAN func ResourceCapsManChannel() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/caps-man/channel"), MetaId: PropId(Id), "band": { Type: schema.TypeString, Optional: true, Description: "Define operational radio frequency band and mode taken from hardware capability of wireless card.", ValidateFunc: validation.StringInSlice([]string{"2ghz-b", "2ghz-b/g", "2ghz-b/g/n", "2ghz-g/n", "2ghz-onlyg", "2ghz-onlyn", "5ghz-a", "5ghz-a/n", "5ghz-a/n/ac", "5ghz-n/ac", "5ghz-onlyac", "5ghz-onlyn"}, false), }, KeyComment: PropCommentRw, "control_channel_width": { Type: schema.TypeString, Optional: true, Description: "Control channel width.", ValidateFunc: validation.StringInSlice([]string{"5mhz", "10mhz", "20mhz", "40mhz-turbo"}, false), }, "extension_channel": { Type: schema.TypeString, Optional: true, Description: "Extension channel configuration. (E.g. Ce = extension channel is above Control channel, " + "eC = extension channel is below Control channel)", ValidateFunc: validation.StringInSlice([]string{"Ce", "Ceee", "Ceeeeeee", "eC", "eCee", "eCeeeeee", "eeCe", "eeCeeeee", "eeeC", "eeeCeeee", "eeeeCeee", "eeeeeCee", "eeeeeeCe", "eeeeeeeC", "xx", "xxxx", "xxxxxxxx", "disabled"}, false), }, "frequency": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{ Type: schema.TypeInt, }, Description: "Channel frequency value in MHz on which AP will operate. If left blank, CAPsMAN will " + "automatically determine the best frequency that is least occupied.", }, KeyName: PropNameForceNewRw, "reselect_interval": { Type: schema.TypeString, Optional: true, Description: "The interval after which the least occupied frequency is chosen, can be defined as a random " + "interval, ex. as '30m..60m'. Works only if channel.frequency is left blank.", // We may need to write a custom DiffSuppressFunc. // DiffSuppressFunc: TimeEquall, not for time ranges }, "save_selected": { Type: schema.TypeBool, Optional: true, Description: "If channel frequency is chosen automatically and channel.reselect-interval is used, then " + "saves the last picked frequency.", }, "secondary_frequency": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "Specifies the second frequency that will be used for 80+80MHz configuration. " + "Set it to Disabled in order to disable 80+80MHz capability.", }, "skip_dfs_channels": { Type: schema.TypeBool, Optional: true, Description: "If channel.frequency is left blank, the selection will skip DFS channels.", }, "tx_power": { Type: schema.TypeInt, Optional: true, Description: "TX Power for CAP interface (for the whole interface not for individual chains) in dBm. " + "It is not possible to set higher than allowed by country regulations or interface. By " + "default max allowed by country or interface is used.", ValidateFunc: validation.IntBetween(-30, 40), }, "width": { Type: schema.TypeString, Optional: true, Description: "Channel Width in MHz.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, SchemaVersion: 2, StateUpgraders: []schema.StateUpgrader{ { Type: ResourceCapsManChannelV0().CoreConfigSchema().ImpliedType(), Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), Version: 0, }, { Type: ResourceCapsManChannelV1().CoreConfigSchema().ImpliedType(), Upgrade: stateMigrationScalarToList("frequency", "secondary_frequency"), Version: 1, }, }, } } ================================================ FILE: routeros/resource_capsman_channel_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testCapsManChannelMaxVersion = "7.12.2" const testCapsManChannelAddress = "routeros_capsman_channel.test_channel" func TestAccCapsManChannelTest_basic(t *testing.T) { if !testCheckMaxVersion(t, testCapsManChannelMaxVersion) { t.Logf("Test skipped, the maximum supported version is %v", testCapsManChannelMaxVersion) return } for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/caps-man/channel", "routeros_capsman_channel"), Steps: []resource.TestStep{ { Config: testAccCapsManChannelConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testCapsManChannelAddress), resource.TestCheckResourceAttr(testCapsManChannelAddress, "name", "test_channel"), ), }, }, }) }) } } func testAccCapsManChannelConfig() string { return providerConfig + ` resource "routeros_capsman_channel" "test_channel" { name = "test_channel" comment = "test_channel" band = "2ghz-b/g/n" control_channel_width = "10mhz" extension_channel = "eCee" frequency = [2412] reselect_interval = "1h" save_selected = true secondary_frequency = ["disabled"] skip_dfs_channels = true tx_power = 20 } ` } ================================================ FILE: routeros/resource_capsman_channel_v0.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) func ResourceCapsManChannelV0() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/caps-man/channel"), MetaId: PropId(Name), "band": { Type: schema.TypeString, Optional: true, Description: "Define operational radio frequency band and mode taken from hardware capability of wireless card.", ValidateFunc: validation.StringInSlice([]string{"2ghz-b", "2ghz-b/g", "2ghz-b/g/n", "2ghz-g/n", "2ghz-onlyg", "2ghz-onlyn", "5ghz-a", "5ghz-a/n", "5ghz-a/n/ac", "5ghz-n/ac", "5ghz-onlyac", "5ghz-onlyn"}, false), }, KeyComment: PropCommentRw, "control_channel_width": { Type: schema.TypeString, Optional: true, Description: "Control channel width.", ValidateFunc: validation.StringInSlice([]string{"5mhz", "10mhz", "20mhz", "40mhz-turbo"}, false), }, "extension_channel": { Type: schema.TypeString, Optional: true, Description: "Extension channel configuration. (E.g. Ce = extension channel is above Control channel, " + "eC = extension channel is below Control channel)", ValidateFunc: validation.StringInSlice([]string{"Ce", "Ceee", "Ceeeeeee", "eC", "eCee", "eCeeeeee", "eeCe", "eeCeeeee", "eeeC", "eeeCeeee", "eeeeCeee", "eeeeeCee", "eeeeeeCe", "eeeeeeeC", "xx", "xxxx", "xxxxxxxx", "disabled"}, false), }, "frequency": { Type: schema.TypeInt, Optional: true, Computed: true, Description: "Channel frequency value in MHz on which AP will operate. If left blank, CAPsMAN will " + "automatically determine the best frequency that is least occupied.", }, KeyName: PropNameForceNewRw, "reselect_interval": { Type: schema.TypeString, Optional: true, Description: "The interval after which the least occupied frequency is chosen, can be defined as a random " + "interval, ex. as '30m..60m'. Works only if channel.frequency is left blank.", // We may need to write a custom DiffSuppressFunc. // DiffSuppressFunc: TimeEquall, not for time ranges }, "save_selected": { Type: schema.TypeBool, Optional: true, Description: "If channel frequency is chosen automatically and channel.reselect-interval is used, then " + "saves the last picked frequency.", }, "secondary_frequency": { Type: schema.TypeString, Optional: true, Description: "Specifies the second frequency that will be used for 80+80MHz configuration. " + "Set it to Disabled in order to disable 80+80MHz capability.", }, "skip_dfs_channels": { Type: schema.TypeBool, Optional: true, Description: "If channel.frequency is left blank, the selection will skip DFS channels.", }, "tx_power": { Type: schema.TypeInt, Optional: true, Description: "TX Power for CAP interface (for the whole interface not for individual chains) in dBm. " + "It is not possible to set higher than allowed by country regulations or interface. By " + "default max allowed by country or interface is used.", ValidateFunc: validation.IntBetween(-30, 40), }, "width": { Type: schema.TypeString, Optional: true, Description: "Channel Width in MHz.", }, }, } } ================================================ FILE: routeros/resource_capsman_channel_v1.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "band": "2ghz-b", "control-channel-width": "5mhz", "extension-channel": "disabled", "frequency": "2112", "name": "channel1", "reselect-interval": "10m", "save-selected": "true", "secondary-frequency": "disabled", "skip-dfs-channels": "true", "tx-power": "-20" } */ // https://help.mikrotik.com/docs/display/ROS/CAPsMAN func ResourceCapsManChannelV1() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/caps-man/channel"), MetaId: PropId(Id), "band": { Type: schema.TypeString, Optional: true, Description: "Define operational radio frequency band and mode taken from hardware capability of wireless card.", ValidateFunc: validation.StringInSlice([]string{"2ghz-b", "2ghz-b/g", "2ghz-b/g/n", "2ghz-g/n", "2ghz-onlyg", "2ghz-onlyn", "5ghz-a", "5ghz-a/n", "5ghz-a/n/ac", "5ghz-n/ac", "5ghz-onlyac", "5ghz-onlyn"}, false), }, KeyComment: PropCommentRw, "control_channel_width": { Type: schema.TypeString, Optional: true, Description: "Control channel width.", ValidateFunc: validation.StringInSlice([]string{"5mhz", "10mhz", "20mhz", "40mhz-turbo"}, false), }, "extension_channel": { Type: schema.TypeString, Optional: true, Description: "Extension channel configuration. (E.g. Ce = extension channel is above Control channel, " + "eC = extension channel is below Control channel)", ValidateFunc: validation.StringInSlice([]string{"Ce", "Ceee", "Ceeeeeee", "eC", "eCee", "eCeeeeee", "eeCe", "eeCeeeee", "eeeC", "eeeCeeee", "eeeeCeee", "eeeeeCee", "eeeeeeCe", "eeeeeeeC", "xx", "xxxx", "xxxxxxxx", "disabled"}, false), }, "frequency": { Type: schema.TypeInt, Optional: true, Computed: true, Description: "Channel frequency value in MHz on which AP will operate. If left blank, CAPsMAN will " + "automatically determine the best frequency that is least occupied.", }, KeyName: PropNameForceNewRw, "reselect_interval": { Type: schema.TypeString, Optional: true, Description: "The interval after which the least occupied frequency is chosen, can be defined as a random " + "interval, ex. as '30m..60m'. Works only if channel.frequency is left blank.", // We may need to write a custom DiffSuppressFunc. // DiffSuppressFunc: TimeEquall, not for time ranges }, "save_selected": { Type: schema.TypeBool, Optional: true, Description: "If channel frequency is chosen automatically and channel.reselect-interval is used, then " + "saves the last picked frequency.", }, "secondary_frequency": { Type: schema.TypeString, Optional: true, Description: "Specifies the second frequency that will be used for 80+80MHz configuration. " + "Set it to Disabled in order to disable 80+80MHz capability.", }, "skip_dfs_channels": { Type: schema.TypeBool, Optional: true, Description: "If channel.frequency is left blank, the selection will skip DFS channels.", }, "tx_power": { Type: schema.TypeInt, Optional: true, Description: "TX Power for CAP interface (for the whole interface not for individual chains) in dBm. " + "It is not possible to set higher than allowed by country regulations or interface. By " + "default max allowed by country or interface is used.", ValidateFunc: validation.IntBetween(-30, 40), }, "width": { Type: schema.TypeString, Optional: true, Description: "Channel Width in MHz.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_capsman_configuration.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "channel": "channel-cfg", "comment": "Comment", "country": "no_country_set", "datapath": "datapath-cfg", "disconnect-timeout": "1s150ms", "distance": "indoors", "frame-lifetime": "120ms", "guard-interval": "long", "hide-ssid": "true", "hw-protection-mode": "rts-cts", "hw-retries": "1", "installation": "indoor", "keepalive-frames": "enabled", "load-balancing-group": "", "max-sta-count": "1", "mode": "ap", "multicast-helper": "full", "name": "cfg1", "rates": "rate-cfg", "rx-chains": "1,3", "security": "security-cfg", "ssid": "SSID", "tx-chains": "0,2" } */ // https://help.mikrotik.com/docs/display/ROS/CAPsMAN func ResourceCapsManConfiguration() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/caps-man/configuration"), MetaId: PropId(Id), MetaTransformSet: PropTransformSet("channel.config: channel", "datapath.config: datapath", "rates.config: rates", "security.config: security"), "channel": { Type: schema.TypeMap, Optional: true, Description: "Channel inline settings.", Elem: &schema.Schema{ Type: schema.TypeString, }, ValidateDiagFunc: ValidationMapKeyNames, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyComment: PropCommentRw, "country": { Type: schema.TypeString, Optional: true, Description: "Limits available bands, frequencies and maximum transmit power for each frequency. Also " + "specifies default value of scan-list. Value no_country_set is an FCC compliant set of channels.", }, "datapath": { Type: schema.TypeMap, Optional: true, Description: "Datapath inline settings.", Elem: &schema.Schema{ Type: schema.TypeString, }, ValidateDiagFunc: ValidationMapKeyNames, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "disconnect_timeout": { Type: schema.TypeString, Optional: true, Description: "This interval is measured from third sending failure on the lowest data rate. At this point " + "3 * (hw-retries + 1) frame transmits on the lowest data rate had failed. During disconnect-timeout packet " + "transmission will be retried with on-fail-retry-time interval. If no frame can be transmitted successfully " + `during disconnect-timeout, the connection is closed, and this event is logged as "extensive data loss". ` + "Successful frame transmission resets this timer.", DiffSuppressFunc: TimeEqual, }, "distance": { Type: schema.TypeString, Optional: true, Description: "How long to wait for confirmation of unicast frames (ACKs) before considering transmission " + "unsuccessful, or in short ACK-Timeout.", }, "frame_lifetime": { Type: schema.TypeString, Optional: true, Description: "Discard frames that have been queued for sending longer than frame-lifetime. By default, when " + "value of this property is 0, frames are discarded only after connection is closed (format: 0.00 sec).", DiffSuppressFunc: TimeEqual, }, "guard_interval": { Type: schema.TypeString, Optional: true, Description: "Whether to allow use of short guard interval (refer to 802.11n MCS specification to see how " + `this may affect throughput). "any" will use either short or long, depending on data rate, "long" will ` + "use long.", ValidateFunc: validation.StringInSlice([]string{"any ", "long"}, false), }, "hide_ssid": { Type: schema.TypeBool, Optional: true, Computed: true, Description: "This property has effect only in AP mode. Setting it to yes can remove this network from " + "the list of wireless networks that are shown by some client software. Changing this setting does not " + "improve the security of the wireless network, because SSID is included in other frames sent by the AP.", }, "hw_protection_mode": { Type: schema.TypeString, Optional: true, Description: "Frame protection support property. " + "[See docs](https://wiki.mikrotik.com/wiki/Manual:Interface/Wireless#Frame_protection_support_(RTS/CTS)).", ValidateFunc: validation.StringInSlice([]string{"cts-to-self", "none", "rts-cts"}, false), }, "hw_retries": { Type: schema.TypeInt, Optional: true, Description: "Number of times sending frame is retried without considering it a transmission failure. " + "[See docs](https://wiki.mikrotik.com/wiki/Manual:Interface/Wireless)", ValidateFunc: validation.IntBetween(0, 15), }, "installation": { Type: schema.TypeString, Optional: true, Description: "Adjusts scan-list to use indoor, outdoor or all frequencies for the country that is set.", ValidateFunc: validation.StringInSlice([]string{"any", "indoor", "outdoor"}, false), }, "keepalive_frames": { Type: schema.TypeString, Optional: true, Description: `If a client has not communicated for around 20 seconds, AP sends a "keepalive-frame".`, ValidateFunc: validation.StringInSlice([]string{"enabled", "disabled"}, false), }, "load_balancing_group": { Type: schema.TypeString, Optional: true, Description: "Tags the interface to the load balancing group. For a client to connect to interface in this " + "group, the interface should have the same number of already connected clients as all other interfaces " + "in the group or smaller. Useful in setups where ranges of CAPs mostly overlap.", }, "max_sta_count": { Type: schema.TypeInt, Optional: true, Description: "Maximum number of associated clients.", ValidateFunc: validation.IntBetween(1, 2007), }, "mode": { Type: schema.TypeString, Optional: true, Description: "Set operational mode. Only **ap** currently supported.", ValidateFunc: validation.StringInSlice([]string{"ap"}, false), }, KeyName: PropNameForceNewRw, "multicast_helper": { Type: schema.TypeString, Optional: true, Description: "When set to full multicast packets will be sent with unicast destination MAC address, " + "resolving multicast problem on a wireless link. This option should be enabled only on the access " + "point, clients should be configured in station-bridge mode.", ValidateFunc: validation.StringInSlice([]string{"default", "disabled", "full"}, false), }, "rates": { Type: schema.TypeMap, Optional: true, Description: "Rates inline settings.", Elem: &schema.Schema{ Type: schema.TypeString, }, ValidateDiagFunc: ValidationMapKeyNames, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "rx_chains": { Type: schema.TypeList, Optional: true, Description: "Which antennas to use for receive.", Elem: &schema.Schema{ Type: schema.TypeInt, ValidateFunc: validation.IntBetween(0, 3), }, }, "security": { Type: schema.TypeMap, Optional: true, Description: "Security inline settings.", Elem: &schema.Schema{ Type: schema.TypeString, }, ValidateDiagFunc: ValidationMapKeyNames, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "ssid": { Type: schema.TypeString, Optional: true, Description: "SSID (service set identifier) is a name broadcast in the beacons that identifies " + "wireless network.", ValidateFunc: validation.StringLenBetween(0, 32), }, "tx_chains": { Type: schema.TypeList, Optional: true, Description: "Which antennas to use for transmit.", Elem: &schema.Schema{ Type: schema.TypeInt, ValidateFunc: validation.IntBetween(0, 3), }, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, SchemaVersion: 1, StateUpgraders: []schema.StateUpgrader{ { Type: ResourceCapsManConfigurationV0().CoreConfigSchema().ImpliedType(), Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), Version: 0, }, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_capsman_configuration_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testCapsManConfigurationMaxVersion = "7.12.2" const testCapsManConfigurationAddress = "routeros_capsman_configuration.test_configuration" func TestAccCapsManConfigurationTest_basic(t *testing.T) { if !testCheckMaxVersion(t, testCapsManConfigurationMaxVersion) { t.Logf("Test skipped, the maximum supported version is %v", testCapsManConfigurationMaxVersion) return } for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/caps-man/configuration", "routeros_capsman_configuration"), Steps: []resource.TestStep{ { Config: testAccCapsManConfigurationConfig(0), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testCapsManConfigurationAddress), resource.TestCheckResourceAttr(testCapsManConfigurationAddress, "name", "test_configuration"), ), }, { Config: testAccCapsManConfigurationConfig(1), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("routeros_capsman_configuration.test_configuration_2", "name", "test_configuration_2"), resource.TestCheckResourceAttrSet("routeros_capsman_configuration.test_configuration_2", "channel.config"), resource.TestCheckResourceAttrSet("routeros_capsman_configuration.test_configuration_2", "datapath.config"), resource.TestCheckResourceAttrSet("routeros_capsman_configuration.test_configuration_2", "rates.config"), resource.TestCheckResourceAttrSet("routeros_capsman_configuration.test_configuration_2", "security.config"), ), Destroy: false, }, }, }) }) } } func testAccCapsManConfigurationConfig(n int) string { tests := []string{` resource "routeros_capsman_configuration" "test_configuration" { comment = "Comment" country = "no_country_set" disconnect_timeout = "1s150ms" distance = "indoors" frame_lifetime = "0.12" // 120ms guard_interval = "long" hide_ssid = true hw_protection_mode = "rts-cts" hw_retries = 1 installation = "indoor" keepalive_frames = "enabled" load_balancing_group = "" max_sta_count = 1 mode = "ap" multicast_helper = "full" name = "test_configuration" rx_chains = [1, 3] ssid = "SSID" tx_chains = [0, 2] }`, ` resource "routeros_capsman_channel" "test_channel" { name = "test-channel-config" } resource "routeros_capsman_datapath" "test_datapath" { name = "test-datapath-config" } resource "routeros_capsman_rates" "test_rates" { name = "test-rates-config" } resource "routeros_capsman_security" "test_security" { name = "test-security-config" } resource "routeros_capsman_configuration" "test_configuration_2" { name = "test_configuration_2" channel = { config = "${routeros_capsman_channel.test_channel.name}" band = "2ghz-b/g/n" control_channel_width = "10mhz" extension_channel = "eCee" frequency = 2412 reselect_interval = "1h" save_selected = "true" secondary_frequency = "disabled" skip_dfs_channels = "true" tx_power = 20 } datapath = { config = "${routeros_capsman_datapath.test_datapath.name}" arp = "local-proxy-arp" bridge = "bridge" bridge_cost = "100" bridge_horizon = "200" client_to_client_forwarding = "true" interface_list = "static" l2mtu = "1450" local_forwarding = "true" mtu = "1500" vlan_id = "101" vlan_mode = "no-tag" // openflow_switch = "aaa" } rates = { config = "${routeros_capsman_rates.test_rates.name}" basic = "1Mbps,5.5Mbps,6Mbps,18Mbps,36Mbps,54Mbps" ht_basic_mcs = "mcs-0,mcs-7,mcs-11,mcs-14,mcs-16,mcs-21" ht_supported_mcs = "mcs-3,mcs-8,mcs-10,mcs-13,mcs-17,mcs-18" supported = "2Mbps,11Mbps,9Mbps,12Mbps,24Mbps,48Mbps" vht_basic_mcs = "none" vht_supported_mcs = "mcs0-9,mcs0-7" } security = { config = "${routeros_capsman_security.test_security.name}" authentication_types = "wpa-psk,wpa-eap" disable_pmkid = "true" eap_methods = "eap-tls,passthrough" eap_radius_accounting = "true" encryption = "aes-ccm,tkip" group_encryption = "aes-ccm" group_key_update = "1h" passphrase = "AAAAAAAAA" tls_certificate = "none" tls_mode = "verify-certificate" } depends_on = [ routeros_capsman_channel.test_channel, routeros_capsman_datapath.test_datapath, routeros_capsman_rates.test_rates, routeros_capsman_security.test_security ] }`, } return providerConfig + tests[n] } ================================================ FILE: routeros/resource_capsman_configuration_v0.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) func ResourceCapsManConfigurationV0() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/caps-man/configuration"), MetaId: PropId(Name), MetaTransformSet: PropTransformSet("channel.config: channel", "datapath.config: datapath", "rates.config: rates", "security.config: security"), "channel": { Type: schema.TypeMap, Optional: true, Description: "Channel inline settings.", Elem: &schema.Schema{ Type: schema.TypeString, }, ValidateDiagFunc: ValidationMapKeyNames, }, KeyComment: PropCommentRw, "country": { Type: schema.TypeString, Optional: true, Description: "Limits available bands, frequencies and maximum transmit power for each frequency. Also " + "specifies default value of scan-list. Value no_country_set is an FCC compliant set of channels.", }, "datapath": { Type: schema.TypeMap, Optional: true, Description: "Datapath inline settings.", Elem: &schema.Schema{ Type: schema.TypeString, }, ValidateDiagFunc: ValidationMapKeyNames, }, "disconnect_timeout": { Type: schema.TypeString, Optional: true, Description: "This interval is measured from third sending failure on the lowest data rate. At this point " + "3 * (hw-retries + 1) frame transmits on the lowest data rate had failed. During disconnect-timeout packet " + "transmission will be retried with on-fail-retry-time interval. If no frame can be transmitted successfully " + `during disconnect-timeout, the connection is closed, and this event is logged as "extensive data loss". ` + "Successful frame transmission resets this timer.", DiffSuppressFunc: TimeEqual, }, "distance": { Type: schema.TypeString, Optional: true, Description: "How long to wait for confirmation of unicast frames (ACKs) before considering transmission " + "unsuccessful, or in short ACK-Timeout.", }, "frame_lifetime": { Type: schema.TypeString, Optional: true, Description: "Discard frames that have been queued for sending longer than frame-lifetime. By default, when " + "value of this property is 0, frames are discarded only after connection is closed (format: 0.00 sec).", DiffSuppressFunc: TimeEqual, }, "guard_interval": { Type: schema.TypeString, Optional: true, Description: "Whether to allow use of short guard interval (refer to 802.11n MCS specification to see how " + `this may affect throughput). "any" will use either short or long, depending on data rate, "long" will ` + "use long.", ValidateFunc: validation.StringInSlice([]string{"any ", "long"}, false), }, "hide_ssid": { Type: schema.TypeBool, Optional: true, Computed: true, Description: "This property has effect only in AP mode. Setting it to yes can remove this network from " + "the list of wireless networks that are shown by some client software. Changing this setting does not " + "improve the security of the wireless network, because SSID is included in other frames sent by the AP.", }, "hw_protection_mode": { Type: schema.TypeString, Optional: true, Description: "Frame protection support property. " + "[See docs](https://wiki.mikrotik.com/wiki/Manual:Interface/Wireless#Frame_protection_support_(RTS/CTS)).", ValidateFunc: validation.StringInSlice([]string{"cts-to-self", "none", "rts-cts"}, false), }, "hw_retries": { Type: schema.TypeInt, Optional: true, Description: "Number of times sending frame is retried without considering it a transmission failure. " + "[See docs](https://wiki.mikrotik.com/wiki/Manual:Interface/Wireless)", ValidateFunc: validation.IntBetween(0, 15), }, "installation": { Type: schema.TypeString, Optional: true, Description: "Adjusts scan-list to use indoor, outdoor or all frequencies for the country that is set.", ValidateFunc: validation.StringInSlice([]string{"any", "indoor", "outdoor"}, false), }, "keepalive_frames": { Type: schema.TypeString, Optional: true, Description: `If a client has not communicated for around 20 seconds, AP sends a "keepalive-frame".`, ValidateFunc: validation.StringInSlice([]string{"enabled", "disabled"}, false), }, "load_balancing_group": { Type: schema.TypeString, Optional: true, Description: "Tags the interface to the load balancing group. For a client to connect to interface in this " + "group, the interface should have the same number of already connected clients as all other interfaces " + "in the group or smaller. Useful in setups where ranges of CAPs mostly overlap.", }, "max_sta_count": { Type: schema.TypeInt, Optional: true, Description: "Maximum number of associated clients.", ValidateFunc: validation.IntBetween(1, 2007), }, "mode": { Type: schema.TypeString, Optional: true, Description: "Set operational mode. Only **ap** currently supported.", ValidateFunc: validation.StringInSlice([]string{"ap"}, false), }, KeyName: PropNameForceNewRw, "multicast_helper": { Type: schema.TypeString, Optional: true, Description: "When set to full multicast packets will be sent with unicast destination MAC address, " + "resolving multicast problem on a wireless link. This option should be enabled only on the access " + "point, clients should be configured in station-bridge mode.", ValidateFunc: validation.StringInSlice([]string{"default", "disabled", "full"}, false), }, "rates": { Type: schema.TypeMap, Optional: true, Description: "Rates inline settings.", Elem: &schema.Schema{ Type: schema.TypeString, }, ValidateDiagFunc: ValidationMapKeyNames, }, "rx_chains": { Type: schema.TypeList, Optional: true, Description: "Which antennas to use for receive.", Elem: &schema.Schema{ Type: schema.TypeInt, ValidateFunc: validation.IntBetween(0, 3), }, }, "security": { Type: schema.TypeMap, Optional: true, Description: "Security inline settings.", Elem: &schema.Schema{ Type: schema.TypeString, }, ValidateDiagFunc: ValidationMapKeyNames, }, "ssid": { Type: schema.TypeString, Optional: true, Description: "SSID (service set identifier) is a name broadcast in the beacons that identifies " + "wireless network.", ValidateFunc: validation.StringLenBetween(0, 32), }, "tx_chains": { Type: schema.TypeList, Optional: true, Description: "Which antennas to use for transmit.", Elem: &schema.Schema{ Type: schema.TypeInt, ValidateFunc: validation.IntBetween(0, 3), }, }, }, } } ================================================ FILE: routeros/resource_capsman_datapath.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "arp": "disabled", "bridge": "bridge", "bridge-cost": "0", "bridge-horizon": "none", "client-to-client-forwarding": "true", "interface-list": "all", "l2mtu": "0", "local-forwarding": "true", "mtu": "32", "name": "datapath1", "vlan-id": "1", "vlan-mode": "no-tag" } */ // https://help.mikrotik.com/docs/display/ROS/CAPsMAN func ResourceCapsManDatapath() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/caps-man/datapath"), MetaId: PropId(Id), "arp": { Type: schema.TypeString, Optional: true, Description: "ARP mode. See [docs](https://wiki.mikrotik.com/wiki/Manual:IP/ARP#ARP_Modes) for info.", ValidateFunc: validation.StringInSlice([]string{"disabled", "enabled", "local-proxy-arp", "proxy-arp", "reply-only"}, false), }, "bridge": { Type: schema.TypeString, Optional: true, Description: "Bridge to which particular interface should be automatically added as port. Required " + "only when local-forwarding is not used.", }, "bridge_cost": { Type: schema.TypeInt, Optional: true, Description: "Bridge port cost to use when adding as bridge port.", }, "bridge_horizon": { Type: schema.TypeInt, Optional: true, Description: "Bridge horizon to use when adding as bridge port.", }, "client_to_client_forwarding": { Type: schema.TypeBool, Optional: true, Description: "Controls if client-to-client forwarding between wireless clients connected to interface " + "should be allowed, in local forwarding mode this function is performed by CAP, otherwise it is " + "performed by CAPsMAN.", }, KeyComment: PropCommentRw, "interface_list": { Type: schema.TypeString, Optional: true, Description: "Interface list name.", }, "l2mtu": { Type: schema.TypeInt, Optional: true, Description: "Layer2 MTU size.", }, "local_forwarding": { Type: schema.TypeBool, Optional: true, Description: "Controls forwarding mode. If disabled, all L2 and L3 data will be forwarded to CAPsMAN, " + "and further forwarding decisions will be made only then. See [docs](https://wiki.mikrotik.com/wiki/Manual:CAPsMAN#Local_Forwarding_Mode) for info.", }, KeyName: PropNameForceNewRw, "mtu": { Type: schema.TypeInt, Optional: true, Description: "MTU size.", }, "openflow_switch": { Type: schema.TypeString, Optional: true, Description: "OpenFlow switch to add interface to, as port when enabled.", }, KeyVlanId: PropVlanIdRw("VLAN ID to assign to interface if vlan-mode enables use of VLAN tagging.", false), "vlan_mode": { Type: schema.TypeString, Optional: true, Description: "VLAN tagging mode specifies if VLAN tag should be assigned to interface (causes all received " + "data to get tagged with VLAN tag and allows interface to only send out data tagged with given tag)", ValidateFunc: validation.StringInSlice([]string{"no-tag", "use-service-tag", "use-tag"}, false), }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, SchemaVersion: 1, StateUpgraders: []schema.StateUpgrader{ { Type: ResourceCapsManDatapathV0().CoreConfigSchema().ImpliedType(), Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), Version: 0, }, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_capsman_datapath_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testCapsManDatapathMaxVersion = "7.12.2" const testCapsManDatapathAddress = "routeros_capsman_datapath.test_datapath" func TestAccCapsManDatapathTest_basic(t *testing.T) { if !testCheckMaxVersion(t, testCapsManDatapathMaxVersion) { t.Logf("Test skipped, the maximum supported version is %v", testCapsManDatapathMaxVersion) return } for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/caps-man/datapath", "routeros_capsman_datapath"), Steps: []resource.TestStep{ { Config: testAccCapsManDatapathConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testCapsManDatapathAddress), resource.TestCheckResourceAttr(testCapsManDatapathAddress, "name", "test_datapath"), ), }, }, }) }) } } func testAccCapsManDatapathConfig() string { return providerConfig + ` resource "routeros_capsman_datapath" "test_datapath" { name = "test_datapath" comment = "test_datapath" arp = "local-proxy-arp" bridge = "bridge" bridge_cost = 100 bridge_horizon = 200 client_to_client_forwarding = true interface_list = "static" l2mtu = 1450 local_forwarding = true mtu = 1500 vlan_id = 101 vlan_mode = "no-tag" // openflow_switch = "aaa" } ` } ================================================ FILE: routeros/resource_capsman_datapath_v0.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) func ResourceCapsManDatapathV0() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/caps-man/datapath"), MetaId: PropId(Name), "arp": { Type: schema.TypeString, Optional: true, Description: "ARP mode. See [docs](https://wiki.mikrotik.com/wiki/Manual:IP/ARP#ARP_Modes) for info.", ValidateFunc: validation.StringInSlice([]string{"disabled", "enabled", "local-proxy-arp", "proxy-arp", "reply-only"}, false), }, "bridge": { Type: schema.TypeString, Optional: true, Description: "Bridge to which particular interface should be automatically added as port. Required " + "only when local-forwarding is not used.", }, "bridge_cost": { Type: schema.TypeInt, Optional: true, Description: "Bridge port cost to use when adding as bridge port.", }, "bridge_horizon": { Type: schema.TypeInt, Optional: true, Description: "Bridge horizon to use when adding as bridge port.", }, "client_to_client_forwarding": { Type: schema.TypeBool, Optional: true, Description: "Controls if client-to-client forwarding between wireless clients connected to interface " + "should be allowed, in local forwarding mode this function is performed by CAP, otherwise it is " + "performed by CAPsMAN.", }, KeyComment: PropCommentRw, "interface_list": { Type: schema.TypeString, Optional: true, Description: "Interface list name.", }, "l2mtu": { Type: schema.TypeInt, Optional: true, Description: "Layer2 MTU size.", }, "local_forwarding": { Type: schema.TypeBool, Optional: true, Description: "Controls forwarding mode. If disabled, all L2 and L3 data will be forwarded to CAPsMAN, " + "and further forwarding decisions will be made only then. See [docs](https://wiki.mikrotik.com/wiki/Manual:CAPsMAN#Local_Forwarding_Mode) for info.", }, KeyName: PropNameForceNewRw, "mtu": { Type: schema.TypeInt, Optional: true, Description: "MTU size.", }, "openflow_switch": { Type: schema.TypeString, Optional: true, Description: "OpenFlow switch to add interface to, as port when enabled.", }, KeyVlanId: PropVlanIdRw("VLAN ID to assign to interface if vlan-mode enables use of VLAN tagging.", false), "vlan_mode": { Type: schema.TypeString, Optional: true, Description: "VLAN tagging mode specifies if VLAN tag should be assigned to interface (causes all received " + "data to get tagged with VLAN tag and allows interface to only send out data tagged with given tag)", ValidateFunc: validation.StringInSlice([]string{"no-tag", "use-service-tag", "use-tag"}, false), }, }, } } ================================================ FILE: routeros/resource_capsman_interface.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*1", "arp-timeout": "auto", "bound": "true", "configuration": "test", "current-authorized-clients": "0", "current-basic-rate-set": "OFDM:6", "current-channel": "2462/20/gn(30dBm)", "current-rate-set": "OFDM:6-54 BW:1x SGI:1x HT:0-15", "current-registered-clients": "0", "current-state": "running-ap", "disabled": "false", "inactive": "false", "l2mtu": "1600", "mac-address": "00:00:00:00:00:00", "master": "true", "master-interface": "none", "name": "cap1", "radio-mac": "00:00:00:00:00:00", "radio-name": "000000000000", "running": "false" } */ // https://help.mikrotik.com/docs/display/ROS/CAPsMAN func ResourceCapsManInterface() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/caps-man/interface"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("current_authorized_clients", "current_basic_rate_set", "current_channel", "current_rate_set", "current_registered_clients", "current_state"), MetaTransformSet: PropTransformSet("channel.config: channel", "configuration.config: configuration", "datapath.config: datapath", "rates.config: rates", "security.config: security"), KeyArpTimeout: PropArpTimeoutRw, "bound": { Type: schema.TypeBool, Computed: true, Description: "A flag whether the interface is currently available for the CAPsMAN.", }, "channel": { Type: schema.TypeMap, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "Channel inline settings.", ValidateDiagFunc: ValidationMapKeyNames, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "configuration": { Type: schema.TypeMap, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "Configuration inline settings.", ValidateDiagFunc: ValidationMapKeyNames, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyComment: PropCommentRw, "datapath": { Type: schema.TypeMap, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "Datapath inline settings.", ValidateDiagFunc: ValidationMapKeyNames, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyDisabled: PropDisabledRw, "inactive": { Type: schema.TypeBool, Computed: true, Description: "A flag whether the interface is currently inactive.", }, KeyL2Mtu: PropL2MtuRo, "mac_address": { Type: schema.TypeString, Description: "MAC address (BSSID) to use for the interface.", Optional: true, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "master": { Type: schema.TypeBool, Computed: true, Description: "A flag whether the interface is not a virtual one.", }, "master_interface": { Type: schema.TypeString, Optional: true, Default: "none", Description: "The corresponding master interface of the virtual one.", }, KeyName: PropName("Name of the interface."), "radio_mac": { Type: schema.TypeString, Optional: true, Description: "The MAC address of the associated radio.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "radio_name": { Type: schema.TypeString, Optional: true, Description: "Name of the associated radio.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "rates": { Type: schema.TypeMap, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "Rates inline settings.", ValidateDiagFunc: ValidationMapKeyNames, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "running": { Type: schema.TypeBool, Computed: true, Description: "A flag whether the interface has established a link to another device.", }, "security": { Type: schema.TypeMap, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "Security inline settings.", ValidateDiagFunc: ValidationMapKeyNames, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_capsman_manager.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* /caps-man/manager { "ca-certificate": "none", "certificate": "none", "enabled": "false", "generated-ca-certificate":"CAPsMAN-CA-000000000000", "generated-certificate":"CAPsMAN-000000000000", "package-path": "", "require-peer-certificate": "false", "upgrade-policy": "none" } */ // https://help.mikrotik.com/docs/display/ROS/CAPsMAN func ResourceCapsManManager() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/caps-man/manager"), MetaId: PropId(Name), "ca_certificate": { Type: schema.TypeString, Optional: true, Default: "none", Description: "Device CA certificate.", }, "certificate": { Type: schema.TypeString, Optional: true, Default: "none", Description: "Device certificate.", }, KeyEnabled: PropEnabled("Disable or enable CAPsMAN functionality."), "generated_ca_certificate": { Type: schema.TypeString, Computed: true, Description: "Generated CA certificate.", }, "generated_certificate": { Type: schema.TypeString, Computed: true, Description: "Generated CAPsMAN certificate.", }, "package_path": { Type: schema.TypeString, Optional: true, Description: "Folder location for the RouterOS packages. For example, use '/upgrade' to specify the " + "upgrade folder from the files section. If empty string is set, CAPsMAN can use built-in RouterOS " + "packages, note that in this case only CAPs with the same architecture as CAPsMAN will be upgraded.", }, "require_peer_certificate": { Type: schema.TypeBool, Optional: true, Description: "Require all connecting CAPs to have a valid certificate.", }, "upgrade_policy": { Type: schema.TypeString, Optional: true, Default: "none", Description: "Upgrade policy options.", ValidateFunc: validation.StringInSlice([]string{"none", "require-same-version", "suggest-same-version"}, false), }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } /* /caps-man/manager/interface { ".id": "*1", "default": "true", "disabled": "false", "dynamic": "false", "forbid": "false", "interface": "all" } */ // https://wiki.mikrotik.com/wiki/Manual:Simple_CAPsMAN_setup func ResourceCapsManManagerInterface() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/caps-man/manager/interface"), MetaId: PropId(Id), KeyComment: PropCommentRw, KeyDefault: PropDefaultRo, KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, "forbid": { Type: schema.TypeBool, Optional: true, Description: "Disable interface listening.", }, KeyInterface: PropInterfaceRw, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } /* /caps-man/aaa { "called-format": "mac:ssid", "interim-update": "disabled", "mac-caching": "disabled", "mac-format": "XX:XX:XX:XX:XX:XX", "mac-mode": "as-username" } */ // https://help.mikrotik.com/docs/display/ROS/CAPsMAN func ResourceCapsManAaa() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/caps-man/aaa"), MetaId: PropId(Name), "called_format": { Type: schema.TypeString, Optional: true, Default: "mac:ssid", Description: "Format of how the 'called-id' identifier will be passed to RADIUS. When configuring radius " + "server clients, you can specify 'called-id' in order to separate multiple entires.", ValidateFunc: validation.StringInSlice([]string{"mac", "mac:ssid", "ssid"}, false), }, "interim_update": { Type: schema.TypeString, Optional: true, Computed: true, Description: "When RADIUS accounting is used, Access Point periodically sends accounting information " + "updates to the RADIUS server. This property specifies the default update interval that can be " + "overridden by the RADIUS server using the Acct-Interim-Interval attribute.", //DiffSuppressFunc: TimeEquall, // "interim-update": "disabled" }, "mac_caching": { Type: schema.TypeString, Optional: true, Computed: true, Description: "If this value is set to a time interval, the Access Point will cache RADIUS MAC authentication " + "responses for a specified time, and will not contact the RADIUS server if matching cache entry already " + "exists. The value disabled will disable the cache, Access Point will always contact the RADIUS server.", //DiffSuppressFunc: TimeEquall, // "mac-caching": "disabled" }, "mac_format": { Type: schema.TypeString, Optional: true, Default: "XX:XX:XX:XX:XX:XX", Description: "Controls how the MAC address of the client is encoded by Access Point in the User-Name " + "attribute of the MAC authentication and MAC accounting RADIUS requests.", }, "mac_mode": { Type: schema.TypeString, Optional: true, Default: "as-username", Description: "By default Access Point uses an empty password, when sending Access-Request during MAC " + "authentication. When this property is set to as-username-and-password, Access Point will use the same " + "value for the User-Password attribute as for the User-Name attribute.", ValidateFunc: validation.StringInSlice([]string{"as-username", "as-username-and-password"}, false), }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_capsman_manager_test.go ================================================ package routeros import ( "strings" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testCapsManManagerMaxVersion = "7.12.2" const testCapsManAaaAddress = "routeros_capsman_aaa.test_3a" const testCapsManManagerAddress = "routeros_capsman_manager.test_manager" const testCapsManManagerInterfaceAddress = "routeros_capsman_manager_interface.test_manager_interface" func TestAccCapsManManagerTest_basic(t *testing.T) { if !testCheckMaxVersion(t, testCapsManManagerMaxVersion) { t.Logf("Test skipped, the maximum supported version is %v", testCapsManManagerMaxVersion) return } for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccCapsManManagerUnitConfig(name, "routeros_capsman_manager"), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testCapsManManagerAddress), resource.TestCheckResourceAttr(testCapsManManagerAddress, "id", "caps-man.manager"), ), }, { Config: testAccCapsManManagerUnitConfig(name, "routeros_capsman_aaa"), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testCapsManAaaAddress), resource.TestCheckResourceAttr(testCapsManAaaAddress, "id", "caps-man.aaa"), ), }, { Config: testAccCapsManManagerUnitConfig(name, "routeros_capsman_manager_interface"), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testCapsManManagerInterfaceAddress), resource.TestCheckResourceAttr(testCapsManManagerInterfaceAddress, "interface", "ether1"), ), }, }, }) }) } } func testAccCapsManManagerUnitConfig(testName, resourceName string) string { conf := providerConfig switch resourceName { // AAA case "routeros_capsman_aaa": if strings.Contains(testName, "API") { // API conf += ` resource "routeros_capsman_aaa" "test_3a" { called_format = "ssid" mac_mode = "as-username-and-password" } ` } else { // REST conf += ` resource "routeros_capsman_aaa" "test_3a" { called_format = "mac:ssid" mac_mode = "as-username" } ` } // Manager case "routeros_capsman_manager": if strings.Contains(testName, "API") { // API conf += ` resource "routeros_capsman_manager" "test_manager" { enabled = true upgrade_policy = "require-same-version" } ` } else { // REST conf += ` resource "routeros_capsman_manager" "test_manager" { enabled = false upgrade_policy = "none" } ` } // CAPsMAN interfaces case "routeros_capsman_manager_interface": conf += ` resource "routeros_capsman_manager_interface" "test_manager_interface" { interface = "ether1" forbid = true } ` } return conf } ================================================ FILE: routeros/resource_capsman_provisioning.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "action": "none", "common-name-regexp": "", "disabled": "false", "hw-supported-modes": "", "identity-regexp": "", "ip-address-ranges": "", "master-configuration": "cfg1", "name-format": "cap", "name-prefix": "", "radio-mac": "00:00:00:00:00:00", "slave-configurations": "" } */ // https://help.mikrotik.com/docs/display/ROS/CAPsMAN func ResourceCapsManProvisioning() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/caps-man/provisioning"), MetaId: PropId(Id), "action": { Type: schema.TypeString, Optional: true, Default: "none", Description: "Provisioning action.", ValidateFunc: validation.StringInSlice([]string{"create-disabled", "create-enabled", "create-dynamic-enabled", "none"}, false), }, KeyComment: PropCommentRw, "common_name_regexp": { Type: schema.TypeString, Optional: true, Description: "Regular expression to match radios by common name. Each CAP's common name identifier can be " + `found under "/caps-man radio" as value "REMOTE-CAP-NAME"`, }, KeyDisabled: PropDisabledRw, "hw_supported_modes": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"a", "a-turbo", "ac", "an", "b", "g", "g-turbo", "gn"}, false), }, Description: "Match radios by supported wireless modes.", }, "identity_regexp": { Type: schema.TypeString, Optional: true, Description: "Regular expression to match radios by router identity.", }, "ip_address_ranges": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "Match CAPs with IPs within configured address range.", }, "master_configuration": { Type: schema.TypeString, Required: true, Description: "If action specifies to create interfaces, then a new master interface with its configuration " + "set to this configuration profile will be created", }, "name_format": { Type: schema.TypeString, Optional: true, Default: "cap", Description: "Specify the syntax of the CAP interface name creation.", ValidateFunc: validation.StringInSlice([]string{"cap", "identity", "prefix", "prefix-identity"}, false), }, "name_prefix": { Type: schema.TypeString, Optional: true, Description: "Name prefix which can be used in the name-format for creating the CAP interface names.", }, "radio_mac": { Type: schema.TypeString, Optional: true, Default: "00:00:00:00:00:00", Description: "MAC address of radio to be matched, empty MAC (00:00:00:00:00:00) means match all MAC addresses.", ValidateFunc: ValidationMacAddress, }, "slave_configurations": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "If action specifies to create interfaces, then a new slave interface for each configuration " + "profile in this list is created.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, SchemaVersion: 1, StateUpgraders: []schema.StateUpgrader{ { Type: ResourceCapsManProvisioningV0().CoreConfigSchema().ImpliedType(), Upgrade: stateMigrationScalarToList("hw_supported_modes", "ip_address_ranges", "slave_configurations"), Version: 0, }, }, } } ================================================ FILE: routeros/resource_capsman_provisioning_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testCapsManProvisioningMaxVersion = "7.12.2" const testCapsManProvisioningAddress = "routeros_capsman_provisioning.test_provisioning" func TestAccCapsManProvisioningTest_basic(t *testing.T) { if !testCheckMaxVersion(t, testCapsManProvisioningMaxVersion) { t.Logf("Test skipped, the maximum supported version is %v", testCapsManProvisioningMaxVersion) return } for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/caps-man/provisioning", "routeros_capsman_provisioning"), Steps: []resource.TestStep{ { Config: testAccCapsManProvisioningConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testCapsManProvisioningAddress), resource.TestCheckResourceAttr(testCapsManProvisioningAddress, "name_prefix", "cap-"), ), }, }, }) }) } } func testAccCapsManProvisioningConfig() string { return providerConfig + ` resource "routeros_capsman_configuration" "test_configuration" { name = "cfg1" } resource "routeros_capsman_provisioning" "test_provisioning" { master_configuration = "cfg1" action = "create-disabled" name_prefix = "cap-" depends_on = [ routeros_capsman_configuration.test_configuration, ] } ` } ================================================ FILE: routeros/resource_capsman_provisioning_v0.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "action": "none", "common-name-regexp": "", "disabled": "false", "hw-supported-modes": "", "identity-regexp": "", "ip-address-ranges": "", "master-configuration": "cfg1", "name-format": "cap", "name-prefix": "", "radio-mac": "00:00:00:00:00:00", "slave-configurations": "" } */ // https://help.mikrotik.com/docs/display/ROS/CAPsMAN func ResourceCapsManProvisioningV0() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/caps-man/provisioning"), MetaId: PropId(Id), "action": { Type: schema.TypeString, Optional: true, Default: "none", Description: "Provisioning action.", ValidateFunc: validation.StringInSlice([]string{"create-disabled", "create-enabled", "create-dynamic-enabled", "none"}, false), }, KeyComment: PropCommentRw, "common_name_regexp": { Type: schema.TypeString, Optional: true, Description: "Regular expression to match radios by common name. Each CAP's common name identifier can be " + `found under "/caps-man radio" as value "REMOTE-CAP-NAME"`, }, KeyDisabled: PropDisabledRw, "hw_supported_modes": { Type: schema.TypeString, Optional: true, Description: "Match radios by supported wireless modes.", ValidateFunc: validation.StringInSlice([]string{"a", "a-turbo", "ac", "an", "b", "g", "g-turbo", "gn"}, false), }, "identity_regexp": { Type: schema.TypeString, Optional: true, Description: "Regular expression to match radios by router identity.", }, "ip_address_ranges": { Type: schema.TypeString, Optional: true, Description: "Match CAPs with IPs within configured address range.", }, "master_configuration": { Type: schema.TypeString, Required: true, Description: "If action specifies to create interfaces, then a new master interface with its configuration " + "set to this configuration profile will be created", }, "name_format": { Type: schema.TypeString, Optional: true, Default: "cap", Description: "Specify the syntax of the CAP interface name creation.", ValidateFunc: validation.StringInSlice([]string{"cap", "identity", "prefix", "prefix-identity"}, false), }, "name_prefix": { Type: schema.TypeString, Optional: true, Description: "Name prefix which can be used in the name-format for creating the CAP interface names.", }, "radio_mac": { Type: schema.TypeString, Optional: true, Default: "00:00:00:00:00:00", Description: "MAC address of radio to be matched, empty MAC (00:00:00:00:00:00) means match all MAC addresses.", ValidateFunc: ValidationMacAddress, }, "slave_configurations": { Type: schema.TypeString, Optional: true, Description: "If action specifies to create interfaces, then a new slave interface for each configuration " + "profile in this list is created.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_capsman_rates.go ================================================ package routeros import ( "regexp" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "basic": "1Mbps,5.5Mbps,6Mbps,18Mbps,36Mbps,54Mbps", "ht-basic-mcs": "mcs-0,mcs-7,mcs-11,mcs-14,mcs-16,mcs-21", "ht-supported-mcs": "mcs-3,mcs-8,mcs-10,mcs-13,mcs-17,mcs-18", "name": "rate-cfg", "supported": "2Mbps,11Mbps,9Mbps,12Mbps,24Mbps,48Mbps", "vht-basic-mcs": "none", "vht-supported-mcs": "" } */ // https://help.mikrotik.com/docs/display/ROS/CAPsMAN func ResourceCapsManRates() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/caps-man/rates"), MetaId: PropId(Id), "basic": { Type: schema.TypeSet, Optional: true, Description: "List of basic rates. Client will connect to AP only if it supports all basic " + "rates announced by the AP. AP will establish WDS link only if it supports all basic " + "rates of the other AP.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"1Mbps", "2Mbps", "5.5Mbps", "6Mbps", "9Mbps", "11Mbps", "12Mbps", "18Mbps", "24Mbps", "36Mbps", "48Mbps", "54Mbps"}, false), }, }, "supported": { Type: schema.TypeSet, Optional: true, Description: "List of supported rates. Two devices will communicate only using rates that " + "are supported by both devices.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"1Mbps", "2Mbps", "5.5Mbps", "6Mbps", "9Mbps", "11Mbps", "12Mbps", "18Mbps", "24Mbps", "36Mbps", "48Mbps", "54Mbps"}, false), }, }, KeyComment: PropCommentRw, "ht_basic_mcs": { Type: schema.TypeSet, Optional: true, Computed: true, Description: "Modulation and Coding Schemes that every connecting client must support. Refer to " + "802.11n for MCS specification.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringMatch(regexp.MustCompile(`mcs-\d+`), `ht_basic_mcs format is "mcs-[0..23]": mcs-"12"`), }, }, "ht_supported_mcs": { Type: schema.TypeSet, Optional: true, Computed: true, Description: "Modulation and Coding Schemes that this device advertises as supported. Refer to 802.11n " + "for MCS specification.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringMatch(regexp.MustCompile(`mcs-\d+`), `ht_supported_mcs format is "mcs-[0..23]": "mcs-11"`), }, }, KeyName: PropNameForceNewRw, "vht_basic_mcs": { Type: schema.TypeString, Optional: true, Computed: true, Description: "Modulation and Coding Schemes that every connecting client must support. Refer to " + "802.11ac for MCS specification. You can set MCS interval for each of Spatial Stream\n * none " + "- will not use selected\n * MCS 0-7 - client must support MCS-0 to MCS-7\n * MCS " + "0-8 - client must support MCS-0 to MCS-8\n * MCS 0-9 - client must support MCS-0 to MCS-9", ValidateDiagFunc: ValidationMultiValInSlice([]string{"none", "mcs0-7", "mcs0-8", "mcs0-9"}, false, false), }, "vht_supported_mcs": { Type: schema.TypeString, Optional: true, Computed: true, Description: "Modulation and Coding Schemes that this device advertises as supported. Refer to " + "802.11ac for MCS specification. You can set MCS interval for each of Spatial Stream\n * none " + "- will not use selected\n * MCS 0-7 - devices will advertise as supported " + "MCS-0 to MCS-7\n * MCS 0-8 - devices will advertise as supported MCS-0 to MCS-8\n * MCS 0-9 - " + "devices will advertise as supported MCS-0 to MCS-9", ValidateDiagFunc: ValidationMultiValInSlice([]string{"none", "mcs0-7", "mcs0-8", "mcs0-9"}, false, false), }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, SchemaVersion: 1, StateUpgraders: []schema.StateUpgrader{ { Type: ResourceCapsManRatesV0().CoreConfigSchema().ImpliedType(), Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), Version: 0, }, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_capsman_rates_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testCapsManRatesMaxVersion = "7.12.2" const testCapsManRatesAddress = "routeros_capsman_rates.test_rates" func TestAccCapsManRatesTest_basic(t *testing.T) { if !testCheckMaxVersion(t, testCapsManRatesMaxVersion) { t.Logf("Test skipped, the maximum supported version is %v", testCapsManRatesMaxVersion) return } for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/caps-man/rates", "routeros_capsman_rates"), Steps: []resource.TestStep{ { Config: testAccCapsManRatesConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testCapsManRatesAddress), resource.TestCheckResourceAttr(testCapsManRatesAddress, "name", "test_rates"), ), }, }, }) }) } } func testAccCapsManRatesConfig() string { return providerConfig + ` resource "routeros_capsman_rates" "test_rates" { name = "test_rates" comment = "test_rates" basic = ["1Mbps", "5.5Mbps", "6Mbps", "18Mbps", "36Mbps", "54Mbps"] ht_basic_mcs = ["mcs-0", "mcs-7", "mcs-11", "mcs-14", "mcs-16", "mcs-21"] ht_supported_mcs = ["mcs-3", "mcs-8", "mcs-10", "mcs-13", "mcs-17", "mcs-18"] supported = ["2Mbps", "11Mbps", "9Mbps", "12Mbps", "24Mbps", "48Mbps"] vht_basic_mcs = "none" vht_supported_mcs = "mcs0-9,mcs0-7" } ` } ================================================ FILE: routeros/resource_capsman_rates_v0.go ================================================ package routeros import ( "regexp" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) func ResourceCapsManRatesV0() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/caps-man/rates"), MetaId: PropId(Name), "basic": { Type: schema.TypeSet, Optional: true, Description: "List of basic rates. Client will connect to AP only if it supports all basic " + "rates announced by the AP. AP will establish WDS link only if it supports all basic " + "rates of the other AP.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"1Mbps", "2Mbps", "5.5Mbps", "6Mbps", "9Mbps", "11Mbps", "12Mbps", "18Mbps", "24Mbps", "36Mbps", "48Mbps", "54Mbps"}, false), }, }, "supported": { Type: schema.TypeSet, Optional: true, Description: "List of supported rates. Two devices will communicate only using rates that " + "are supported by both devices.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"1Mbps", "2Mbps", "5.5Mbps", "6Mbps", "9Mbps", "11Mbps", "12Mbps", "18Mbps", "24Mbps", "36Mbps", "48Mbps", "54Mbps"}, false), }, }, KeyComment: PropCommentRw, "ht_basic_mcs": { Type: schema.TypeSet, Optional: true, Computed: true, Description: "Modulation and Coding Schemes that every connecting client must support. Refer to " + "802.11n for MCS specification.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringMatch(regexp.MustCompile(`mcs-\d+`), `ht_basic_mcs format is "mcs-[0..23]": mcs-"12"`), }, }, "ht_supported_mcs": { Type: schema.TypeSet, Optional: true, Computed: true, Description: "Modulation and Coding Schemes that this device advertises as supported. Refer to 802.11n " + "for MCS specification.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringMatch(regexp.MustCompile(`mcs-\d+`), `ht_supported_mcs format is "mcs-[0..23]": "mcs-11"`), }, }, KeyName: PropNameForceNewRw, "vht_basic_mcs": { Type: schema.TypeString, Optional: true, Computed: true, Description: "Modulation and Coding Schemes that every connecting client must support. Refer to " + "802.11ac for MCS specification. You can set MCS interval for each of Spatial Stream none " + "- will not use selected Spatial Stream MCS 0-7 - client must support MCS-0 to MCS-7 MCS " + "0-8 - client must support MCS-0 to MCS-8 MCS 0-9 - client must support MCS-0 to MCS-9", ValidateDiagFunc: ValidationMultiValInSlice([]string{"none", "mcs0-7", "mcs0-8", "mcs0-9"}, false, false), }, "vht_supported_mcs": { Type: schema.TypeString, Optional: true, Computed: true, Description: "Modulation and Coding Schemes that this device advertises as supported. Refer to " + "802.11ac for MCS specification. You can set MCS interval for each of Spatial Stream none " + "- will not use selected Spatial Stream MCS 0-7 - devices will advertise as supported " + "MCS-0 to MCS-7 MCS 0-8 - devices will advertise as supported MCS-0 to MCS-8 MCS 0-9 - " + "devices will advertise as supported MCS-0 to MCS-9", ValidateDiagFunc: ValidationMultiValInSlice([]string{"none", "mcs0-7", "mcs0-8", "mcs0-9"}, false, false), }, }, } } ================================================ FILE: routeros/resource_capsman_security.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "authentication-types": "", "disable-pmkid": "false", "eap-methods": "eap-tls", "eap-radius-accounting": "false", "encryption": "", "group-encryption": "aes-ccm", "group-key-update": "1m", "name": "security1", "passphrase": "123123123", "tls-certificate": "none", "tls-mode": "verify-certificate" } */ // https://help.mikrotik.com/docs/display/ROS/CAPsMAN func ResourceCapsManSecurity() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/caps-man/security"), MetaId: PropId(Id), "authentication_types": { Type: schema.TypeSet, Optional: true, Description: "Specify the type of Authentication from wpa-psk, wpa2-psk, wpa-eap or wpa2-eap.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateDiagFunc: ValidationMultiValInSlice([]string{"wpa-psk", "wpa2-psk", "wpa-eap", "wpa2-eap"}, false, false), }, }, KeyComment: PropCommentRw, "disable_pmkid": { Type: schema.TypeBool, Optional: true, Description: "Whether to include PMKID into the EAPOL frame sent out by the Access Point. Disabling PMKID " + "can cause compatibility issues with devices that use the PMKID to connect to an Access Point.", }, "eap_methods": { Type: schema.TypeString, Optional: true, Description: "eap-tls - Use built-in EAP TLS authentication; passthrough - Access point will relay " + "authentication process to the RADIUS server.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"eap-tls", "passthrough"}, false, false), }, "eap_radius_accounting": { Type: schema.TypeBool, Optional: true, Description: "Specifies if RADIUS traffic accounting should be used if RADIUS authentication gets done for " + "this client", }, "encryption": { Type: schema.TypeSet, Optional: true, Description: "Set type of unicast encryption algorithm used.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateDiagFunc: ValidationMultiValInSlice([]string{"aes-ccm", "tkip"}, false, false), }, }, "group_encryption": { Type: schema.TypeString, Optional: true, Description: "Access Point advertises one of these ciphers, multiple values can be selected. Access Point " + "uses it to encrypt all broadcast and multicast frames. Client attempts connection only to Access " + "Points that use one of the specified group ciphers.", ValidateFunc: validation.StringInSlice([]string{"aes-ccm", "tkip"}, false), }, "group_key_update": { Type: schema.TypeString, Optional: true, Description: "Controls how often Access Point updates the group key. This key is used to encrypt all " + "broadcast and multicast frames. property only has effect for Access Points. (30s..1h)", DiffSuppressFunc: TimeEqual, }, KeyName: PropNameForceNewRw, // 802.11i specification: // A pass-phrase is a sequence of between 8 and 63 ASCII-encoded characters. The limit of 63 comes from the // desire to distinguish between a pass-phrase and a PSK displayed as 64 hexadecimal characters. "passphrase": { Type: schema.TypeString, Optional: true, Description: "WPA or WPA2 pre-shared key.", Sensitive: true, ValidateFunc: validation.StringLenBetween(8, 63), }, "tls_certificate": { Type: schema.TypeString, Optional: true, Description: "Access Point always needs a certificate when security.tls-mode is set to value other than " + "no-certificates.", }, "tls_mode": { Type: schema.TypeString, Optional: true, Description: "This property has effect only when security.eap-methods contains eap-tls.", ValidateFunc: validation.StringInSlice([]string{"verify-certificate", "dont-verify-certificate", "no-certificates", "verify-certificate-with-crl"}, false), }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, SchemaVersion: 1, StateUpgraders: []schema.StateUpgrader{ { Type: ResourceCapsManSecurityV0().CoreConfigSchema().ImpliedType(), Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), Version: 0, }, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_capsman_security_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testCapsManSecurityMaxVersion = "7.12.2" const testCapsManSecurityAddress = "routeros_capsman_security.test_security" func TestAccCapsManSecurityTest_basic(t *testing.T) { if !testCheckMaxVersion(t, testCapsManSecurityMaxVersion) { t.Logf("Test skipped, the maximum supported version is %v", testCapsManSecurityMaxVersion) return } for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/caps-man/security", "routeros_capsman_security"), Steps: []resource.TestStep{ { Config: testAccCapsManSecurityConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testCapsManSecurityAddress), resource.TestCheckResourceAttr(testCapsManSecurityAddress, "name", "test_security"), ), }, }, }) }) } } func testAccCapsManSecurityConfig() string { return providerConfig + ` resource "routeros_capsman_security" "test_security" { name = "test_security" comment = "test_security" authentication_types = ["wpa-psk", "wpa-eap", "wpa2-psk"] // Unordered items! disable_pmkid = true eap_methods = "eap-tls,passthrough" eap_radius_accounting = true encryption = ["tkip", "aes-ccm"] // Unordered items! group_encryption = "aes-ccm" group_key_update = "1h" passphrase = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE" // Max length check tls_certificate = "none" tls_mode = "verify-certificate" } ` } ================================================ FILE: routeros/resource_capsman_security_v0.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) func ResourceCapsManSecurityV0() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/caps-man/security"), MetaId: PropId(Name), "authentication_types": { Type: schema.TypeSet, Optional: true, Description: "Specify the type of Authentication from wpa-psk, wpa2-psk, wpa-eap or wpa2-eap.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateDiagFunc: ValidationMultiValInSlice([]string{"wpa-psk", "wpa2-psk", "wpa-eap", "wpa2-eap"}, false, false), }, }, KeyComment: PropCommentRw, "disable_pmkid": { Type: schema.TypeBool, Optional: true, Description: "Whether to include PMKID into the EAPOL frame sent out by the Access Point. Disabling PMKID " + "can cause compatibility issues with devices that use the PMKID to connect to an Access Point.", }, "eap_methods": { Type: schema.TypeString, Optional: true, Description: "eap-tls - Use built-in EAP TLS authentication; passthrough - Access point will relay " + "authentication process to the RADIUS server.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"eap-tls", "passthrough"}, false, false), }, "eap_radius_accounting": { Type: schema.TypeBool, Optional: true, Description: "Specifies if RADIUS traffic accounting should be used if RADIUS authentication gets done for " + "this client", }, "encryption": { Type: schema.TypeSet, Optional: true, Description: "Set type of unicast encryption algorithm used.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateDiagFunc: ValidationMultiValInSlice([]string{"aes-ccm", "tkip"}, false, false), }, }, "group_encryption": { Type: schema.TypeString, Optional: true, Description: "Access Point advertises one of these ciphers, multiple values can be selected. Access Point " + "uses it to encrypt all broadcast and multicast frames. Client attempts connection only to Access " + "Points that use one of the specified group ciphers.", ValidateFunc: validation.StringInSlice([]string{"aes-ccm", "tkip"}, false), }, "group_key_update": { Type: schema.TypeString, Optional: true, Description: "Controls how often Access Point updates the group key. This key is used to encrypt all " + "broadcast and multicast frames. property only has effect for Access Points. (30s..1h)", DiffSuppressFunc: TimeEqual, }, KeyName: PropNameForceNewRw, // 802.11i specification: // A pass-phrase is a sequence of between 8 and 63 ASCII-encoded characters. The limit of 63 comes from the // desire to distinguish between a pass-phrase and a PSK displayed as 64 hexadecimal characters. "passphrase": { Type: schema.TypeString, Optional: true, Description: "WPA or WPA2 pre-shared key.", Sensitive: true, ValidateFunc: validation.StringLenBetween(8, 63), }, "tls_certificate": { Type: schema.TypeString, Optional: true, Description: "Access Point always needs a certificate when security.tls-mode is set to value other than " + "no-certificates.", }, "tls_mode": { Type: schema.TypeString, Optional: true, Description: "This property has effect only when security.eap-methods contains eap-tls.", ValidateFunc: validation.StringInSlice([]string{"verify-certificate", "dont-verify-certificate", "no-certificates", "verify-certificate-with-crl"}, false), }, }, } } ================================================ FILE: routeros/resource_container.go ================================================ package routeros import ( "context" "fmt" "net/url" "strings" "time" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*3", "arch": "amd64", "interface": "veth1", "mounts": "", "name": "76e7fc0c-e2c0-4b8c-b3b0-4c985c496a90", "os": "linux", "repo": "registry-1.docker.io/adguard/adguardhome:latest", "root-dir": "sata4-part1/docker/adg", "status": "stopped", "workdir": "/opt/adguardhome/work" } */ // https://help.mikrotik.com/docs/display/ROS/Container#Container-Properties func ResourceContainer() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/container"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("running", "starting", "stopped", "passed_devs", "stopping", "extracting", "config_json"), "arch": { Type: schema.TypeString, Computed: true, Description: "The architecture of the container image", }, "auto_restart_interval": { Type: schema.TypeString, Optional: true, Description: "Specify an interval at which Container will be restarted on Container failure.", DiffSuppressFunc: TimeEqual, }, "check_certificate": { Type: schema.TypeBool, Optional: true, Description: "Enables trust chain validation from local certificate store.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "cmd": { Type: schema.TypeString, Optional: true, Description: "The main purpose of a CMD is to provide defaults for an executing container. These defaults can include an executable, or they can omit the executable, in which case you must specify an ENTRYPOINT instruction as well.", }, KeyComment: PropCommentRw, "devices": { Type: schema.TypeSet, Optional: true, Description: "Passes through physical device to the container.", Elem: &schema.Schema{ Type: schema.TypeString, }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "dns": { Type: schema.TypeString, Optional: true, Description: "Set custom DNS servers", }, "domain_name": { Type: schema.TypeString, Optional: true, Description: "Container NIS domain name", }, "entrypoint": { Type: schema.TypeString, Optional: true, Description: "An ENTRYPOINT allows to specify executable to run when starting container. Example: /bin/sh", }, "envlist": { Type: schema.TypeString, Optional: true, Description: "list of environmental variables (configured under /container envs ) to be used with container", }, "file": { Type: schema.TypeString, Optional: true, Description: "container *tar.gz tarball if the container is imported from a file", ExactlyOneOf: []string{"file", "remote_image"}, }, "hostname": { Type: schema.TypeString, Optional: true, Description: "Container host name", }, "interface": { Type: schema.TypeString, Required: true, Description: "veth interface to be used with the container", }, "logging": { Type: schema.TypeBool, Optional: true, Description: "if set to yes, all container-generated output will be shown in the RouterOS log", }, "memory_high": { Type: schema.TypeString, Optional: true, Description: "RAM usage limit in bytes for a specific container (string value).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "mounts": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "Mounts from /container/mounts/ sub-menu to be used with this container", }, "name": { Type: schema.TypeString, Computed: true, Description: "Assign a name to the container", }, "os": { Type: schema.TypeString, Computed: true, Description: "The OS of the container image", }, "remote_image": { Type: schema.TypeString, Optional: true, ForceNew: true, Description: "The container image name to be installed if an external registry is used (configured under /container/config set registry-url=...)", ExactlyOneOf: []string{"file", "remote_image"}, DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { if old == "" { return false } if AlwaysPresentNotUserProvided(k, old, new, d) { return true } // Checking the presence of a tag: // ~ remote_image = "traefik/whoami:latest" -> "traefik/whoami" # forces replacement return old == new || old == new+":latest" }, }, "root_dir": { Type: schema.TypeString, Optional: true, Description: "Used to save container store outside main memory", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "running": { Type: schema.TypeBool, Optional: true, Description: "Container state.", Default: true, }, "start_on_boot": { Type: schema.TypeBool, Optional: true, Description: "Start the container on boot", }, "status": { Type: schema.TypeString, Computed: true, Description: "The status of the container", }, "stop_signal": { Type: schema.TypeString, Optional: true, Default: "15-SIGTERM", Description: "Signal to stop the container.", }, "tag": { Type: schema.TypeString, Computed: true, Description: "The tag of the container image", }, "user": { Type: schema.TypeString, Optional: true, Description: "Sets the username used", }, "workdir": { Type: schema.TypeString, Optional: true, Computed: true, Description: "The working directory for cmd entrypoint", }, } resRead := func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { // Run DefaultRead. diags := ResourceRead(ctx, resSchema, d, m) if diags.HasError() { return diags } tag, ok := d.Get("tag").(string) if ok && tag != "" { // Get Registry URL. res, err := ReadItems(nil, "/container/config", m.(Client)) if err != nil { ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgPut, err)) return diag.FromErr(err) } if len(*res) == 0 { return diag.Diagnostics{ diag.Diagnostic{ Severity: diag.Error, Summary: "Failed to retrieve the URL of the container registry, the response is empty", }, } } registryUrl, ok := (*res)[0]["registry-url"] if !ok { // Key name for an unspecified registry. registryUrl, ok = (*res)[0]["assumed-registry-url"] if !ok { return diag.Diagnostics{ diag.Diagnostic{ Severity: diag.Error, Summary: "The `registry-url` was not found in the response", }, } } } u, err := url.Parse(registryUrl) if err != nil { return diag.FromErr(err) } // Remove http(s); host:port; path... for _, item := range []string{u.Scheme, u.Host, u.Path} { tag = strings.TrimPrefix(tag, item) } // Remove (:////)adguard/adguardhome:latest tag = strings.TrimLeft(tag, ":/") d.Set("remote_image", strings.TrimPrefix(tag, registryUrl)) d.Set("running", d.Get("status").(string) == "running") } return nil } resCreate := func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { // Run DefaultCreate. diags := ResourceCreate(ctx, resSchema, d, m) if diags.HasError() { return diags } if d.Get("running").(bool) { startContainer(ctx, resSchema, d, m) } return ResourceRead(ctx, resSchema, d, m) } resUpdate := func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { stopContainer(ctx, resSchema, d, m) // Run DefaultUpdate. diags := ResourceUpdate(ctx, resSchema, d, m) if diags.HasError() { return diags } if d.Get("running").(bool) { startContainer(ctx, resSchema, d, m) } return ResourceRead(ctx, resSchema, d, m) } resDelete := func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { // Stop container stopContainer(ctx, resSchema, d, m) // Run DefaultDelete. return ResourceDelete(ctx, resSchema, d, m) } return &schema.Resource{ CreateContext: resCreate, ReadContext: resRead, UpdateContext: resUpdate, DeleteContext: resDelete, Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(10 * time.Minute), Delete: schema.DefaultTimeout(1 * time.Minute), }, } } func startContainer(ctx context.Context, s map[string]*schema.Schema, d *schema.ResourceData, m interface{}) diag.Diagnostics { stopStateConf := &retry.StateChangeConf{ Pending: []string{"pulling", "extracting"}, Target: []string{"stopped"}, Refresh: func() (result interface{}, state string, err error) { metadata := GetMetadata(s) res, err := ReadItems(&ItemId{metadata.IdType, d.Id()}, metadata.Path, m.(Client)) if err != nil { return res, (*res)[0]["status"], err } return res, (*res)[0]["status"], nil }, Timeout: d.Timeout(schema.TimeoutCreate), } _, err := stopStateConf.WaitForStateContext(ctx) if err != nil { err = fmt.Errorf("error waiting for container instance (%s) to be pulled: %s", d.Id(), err) return diag.FromErr(err) } item := MikrotikItem{"number": d.Id()} // Start container var resUrl = &URL{ Path: s[MetaResourcePath].Default.(string), } if m.(Client).GetTransport() == TransportREST { resUrl.Path += "/start" } err = m.(Client).SendRequest(crudStart, resUrl, item, nil) if err != nil { return diag.FromErr(err) } startStateConf := &retry.StateChangeConf{ Pending: []string{"stopped"}, Target: []string{"running"}, Refresh: func() (result interface{}, state string, err error) { metadata := GetMetadata(s) res, err := ReadItems(&ItemId{metadata.IdType, d.Id()}, metadata.Path, m.(Client)) if err != nil { return res, (*res)[0]["status"], err } return res, (*res)[0]["status"], nil }, Timeout: d.Timeout(schema.TimeoutCreate), } _, err = startStateConf.WaitForStateContext(ctx) if err != nil { err = fmt.Errorf("error waiting for container instance (%s) to be started: %s", d.Id(), err) return diag.FromErr(err) } return nil } func stopContainer(ctx context.Context, s map[string]*schema.Schema, d *schema.ResourceData, m interface{}) diag.Diagnostics { item := MikrotikItem{"number": d.Id()} var resUrl = &URL{ Path: s[MetaResourcePath].Default.(string), } if m.(Client).GetTransport() == TransportREST { resUrl.Path += "/stop" } err := m.(Client).SendRequest(crudStop, resUrl, item, nil) if err != nil { return diag.FromErr(err) } stopStateConf := &retry.StateChangeConf{ Pending: []string{"stopping"}, Target: []string{"stopped"}, Refresh: func() (result interface{}, state string, err error) { metadata := GetMetadata(s) res, err := ReadItems(&ItemId{metadata.IdType, d.Id()}, metadata.Path, m.(Client)) if err != nil { return res, (*res)[0]["status"], err } return res, (*res)[0]["status"], nil }, Timeout: d.Timeout(schema.TimeoutDelete), } _, err = stopStateConf.WaitForStateContext(ctx) if err != nil { err = fmt.Errorf("error waiting for container instance (%s) to be stopped: %s", d.Id(), err) return diag.FromErr(err) } return nil } ================================================ FILE: routeros/resource_container_config.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) // https://help.mikrotik.com/docs/display/ROS/Container#Container-Containerconfiguration func ResourceContainerConfig() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/container/config"), MetaId: PropId(Name), MetaSkipFields: PropSkipFields("assumed_registry_url", "memory_current"), "registry_url": { Type: schema.TypeString, Optional: true, Description: "External registry url from where the container will be downloaded.", }, "username": { Type: schema.TypeString, Optional: true, Description: "Specifies the username for authentication (starting from ROS 7.8)", }, "password": { Type: schema.TypeString, Sensitive: true, Optional: true, Description: "Specifies the password for authentication (starting from ROS 7.8)", }, "ram_high": { Type: schema.TypeString, Optional: true, Description: "RAM usage limit.", DiffSuppressFunc: BytesEqual, }, "tmpdir": { Type: schema.TypeString, Optional: true, Description: "Container extraction directory.", }, "layer_dir": { Type: schema.TypeString, Optional: true, Description: "Container layers directory.", }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_container_envs.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) // https://help.mikrotik.com/docs/display/ROS/Container#Container-Addenvironmentvariablesandmounts(optional) func ResourceContainerEnvs() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/container/envs"), MetaId: PropId(Id), "name": { Type: schema.TypeString, Required: true, Description: "Name of the environment variables list.", }, "key": { Type: schema.TypeString, Required: true, Description: "Key of the environment variable.", }, "value": { Type: schema.TypeString, Required: true, Description: "Value of the environment variable.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } // TODO: cleaner would be if we model it like envlist directly /* resource "routeros_container_envs" "test" { name = "test" env { key = "foo" value = "bar" } env { key = "hello" value = "world" } } resource "routeros_container_envs" "test_foo" { name = "test" key = "foo" value = "bar" } resource "routeros_container_envs" "test_hello" { name = "test" key = "hello" value = "wold" } */ ================================================ FILE: routeros/resource_container_mounts.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) // https://help.mikrotik.com/docs/display/ROS/Container#Container-Addenvironmentvariablesandmounts(optional) func ResourceContainerMounts() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/container/mounts"), MetaId: PropId(Name), "name": { Type: schema.TypeString, Required: true, Description: "Name of the mount.", }, "src": { Type: schema.TypeString, Required: true, Description: "Specifies source path of the mount, which points to a RouterOS location", }, "dst": { Type: schema.TypeString, Required: true, Description: "Specifies destination path of the mount, which points to defined location in container", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_disk_settings.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { "auto-smb-sharing": "false", "auto-smb-user": "guest", "auto-media-sharing": "false", "auto-media-interface": "lo", "default-mount-point-template": "[slot]" } */ // https://help.mikrotik.com/docs/spaces/ROS/pages/91193346/Disks#Disks-Settings func ResourceDiskSettings() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/disk/settings"), MetaId: PropId(Id), "auto_smb_sharing": { Type: schema.TypeBool, Optional: true, Description: "Enables dynamic SMB shares when new disk/partition item is added in '/disk'.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "auto_smb_user": { Type: schema.TypeString, Optional: true, Description: "Default value for smb-sharing/smb-user setting, when new disk/partition item is added in '/disk'.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "auto_media_sharing": { Type: schema.TypeBool, Optional: true, Description: "Enables media dynamically when new disk/partition item is added in '/disk'.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "auto_media_interface": { Type: schema.TypeString, Optional: true, Description: "Interface that will be used in dynamic instance for ip/media when new disk/partition item is added in '/disk'.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "default_mount_point_template": { Type: schema.TypeString, Optional: true, Description: "Sets the default mount point template for each item added in `/disk`.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_disk_settings_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testDiskSettingsTaskMinVersion = "7.17" const testDiskSettingsTask = "routeros_disk_settings.test" func TestAccDiskSettingsTest_basic(t *testing.T) { if !testCheckMinVersion(t, testDiskSettingsTaskMinVersion) { t.Logf("Test skipped, the minimum required version is %v", testDiskSettingsTaskMinVersion) return } for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccDiskSettingsConfig(false, "guest", false, "lo", "[slot]"), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testDiskSettingsTask), resource.TestCheckResourceAttr(testDiskSettingsTask, "auto_smb_sharing", "false"), resource.TestCheckResourceAttr(testDiskSettingsTask, "auto_smb_user", "guest"), resource.TestCheckResourceAttr(testDiskSettingsTask, "auto_media_sharing", "false"), resource.TestCheckResourceAttr(testDiskSettingsTask, "auto_media_interface", "lo"), resource.TestCheckResourceAttr(testDiskSettingsTask, "default_mount_point_template", "[slot]"), ), }, }, }) }) } } func testAccDiskSettingsConfig(autoSmbSharing bool, autoSmbUser string, autoMediaSharing bool, autoMediaInterface string, defaultMountPointTemplate string) string { return fmt.Sprintf(`%v resource "routeros_disk_settings" "test" { auto_smb_sharing = %v auto_smb_user = "%v" auto_media_sharing = %v auto_media_interface = "%v" default_mount_point_template = "%v" } `, providerConfig, autoSmbSharing, autoSmbUser, autoMediaSharing, autoMediaInterface, defaultMountPointTemplate) } ================================================ FILE: routeros/resource_file.go ================================================ package routeros import ( "context" "fmt" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) // https://help.mikrotik.com/docs/pages/viewpage.action?pageId=2555971 func ResourceFile() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/file"), MetaId: PropId(Id), "contents": { Type: schema.TypeString, ForceNew: true, Optional: true, Description: "The actual content of the file", }, "creation_time": { Type: schema.TypeString, Computed: true, Description: "A time when the file was created", }, "last_modified": { Type: schema.TypeString, Computed: true, Description: "A time when the file was modified", }, KeyName: PropName("Name of the file"), "package_architecture": { Type: schema.TypeString, Computed: true, Description: "Architecture that package is built for. Applies only to RouterOS \".npk\" files", }, "package_built_time": { Type: schema.TypeString, Computed: true, Description: "A time when the package was built. Applies only to RouterOS \".npk\" files", }, "package_name": { Type: schema.TypeString, Computed: true, Description: "Name of the installable package. Applies only to RouterOS \".npk\" files", }, "package_version": { Type: schema.TypeString, Computed: true, Description: "A version of the installable package. Applies only to RouterOS \".npk\" files", }, "size": { Type: schema.TypeInt, Computed: true, Description: "File size in bytes", }, "type": { Type: schema.TypeString, Computed: true, Description: "Type of the file. For folders, the file type is the directory", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { metadata := GetMetadata(resSchema) id, err := dynamicIdLookup(metadata.IdType, metadata.Path, m.(Client), d) if err != nil { if err != errorNoLongerExists { ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgDelete, err)) return diag.FromErr(err) } // We inform the user that the resource no longer exists. d.SetId("") return diag.Diagnostics{ diag.Diagnostic{ Severity: diag.Warning, Summary: errorNoLongerExists.Error(), }, } } if diags := fileDelete(ctx, id, m); diags.HasError() { ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgDelete, err)) return diags } d.SetId("") return nil }, Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } func fileCreate(ctx context.Context, name, contents string, m interface{}) (id string, diags diag.Diagnostics) { res, err := CreateItem(ctx, MikrotikItem{"name": name, "contents": contents}, "/file", m.(Client)) if err != nil { ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgPut, err)) diags = diag.FromErr(err) return } id = res.GetID(Id) if id == "" { diags = diag.Diagnostics{ diag.Diagnostic{ Severity: diag.Error, Summary: "The file ID was not found in the response", }, } return } return } func fileDelete(ctx context.Context, id string, m interface{}) diag.Diagnostics { if id == "" { return diag.FromErr(errEmptyId) } url := &URL{Path: "/file"} var data MikrotikItem if m.(Client).GetTransport() == TransportREST { url.Path += "/remove" data = MikrotikItem{".id": id} } else { url.Query = []string{"=.id=" + id} } if err := m.(Client).SendRequest(crudRemove, url, data, nil); err != nil { ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgDelete, err)) return diag.FromErr(err) } return nil } ================================================ FILE: routeros/resource_file_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testFileMinVersion = "7.13" const testFile = "routeros_file.test" func TestAccFileTest_basic(t *testing.T) { if !testCheckMinVersion(t, testFileMinVersion) { t.Logf("Test skipped, the minimum required version is %v", testFileMinVersion) return } for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/file", "routeros_file"), Steps: []resource.TestStep{ { Config: testAccFileConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testFile), resource.TestCheckResourceAttr(testFile, "name", "test"), ), }, }, }) }) } } func testAccFileConfig() string { return providerConfig + ` resource "routeros_file" "test" { name = "test" contents = "This is a test" } ` } ================================================ FILE: routeros/resource_interface_6to4.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*3", "actual-mtu": "1480", "clamp-tcp-mss": "true", "disabled": "false", "dont-fragment": "no", "dscp": "inherit", "local-address": "0.0.0.0", "mtu": "auto", "name": "6to4-tunnel1", "remote-address": "unspecified", "running": "true" } */ // https://help.mikrotik.com/docs/spaces/ROS/pages/135004174/6to4 func ResourceInterface6to4() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/6to4"), MetaId: PropId(Id), KeyActualMtu: PropActualMtuRo, KeyClampTcpMss: PropClampTcpMssRw, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, KeyDontFragment: PropDontFragmentRw, KeyDscp: PropDscpRw, KeyIpsecSecret: PropIpsecSecretRw, KeyKeepalive: PropKeepaliveRw, KeyLocalAddress: PropLocalAddressRw, KeyMtu: PropMtuRw(), KeyName: PropName("Interface name."), KeyRemoteAddress: PropRemoteAddressRw, KeyRunning: PropRunningRo, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_6to4_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterface6to4 = "routeros_interface_6to4.test" func TestAccInterface6to4Test_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/interface/6to4", "routeros_interface_6to4"), Steps: []resource.TestStep{ { Config: testAccInterface6to4Config(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterface6to4), resource.TestCheckResourceAttr(testInterface6to4, "name", "6to4-tunnel1"), resource.TestCheckResourceAttr(testInterface6to4, "keepalive", "10s,10"), ), }, }, }) }) } } func testAccInterface6to4Config() string { return fmt.Sprintf(`%v resource "routeros_interface_6to4" "test" { name = "6to4-tunnel1" keepalive = "10,10" } `, providerConfig) } ================================================ FILE: routeros/resource_interface_bonding.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".about": "802.3ad mode must use mii link-monitoring", ".id": "*B", "arp": "enabled", "arp-interval": "100ms", "arp-ip-targets": "0.0.2.0", "arp-timeout": "auto", "disabled": "false", "down-delay": "10ms", "lacp-rate": "1sec", "lacp-user-key": "123", "link-monitoring": "arp", "mac-address": "52:54:00:12:34:58", "mii-interval": "100ms", "min-links": "1", "mlag-id": "321", "mode": "802.3ad", "mtu": "1500", "name": "bonding1", "primary": "none", "running": "true", "slaves": "ether3,ether4", "transmit-hash-policy": "layer-2-and-3", "up-delay": "10ms" } */ // https://help.mikrotik.com/docs/display/ROS/Bonding#Bonding-PropertyDescription func ResourceInterfaceBonding() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/bonding"), MetaId: PropId(Id), "arp": { Type: schema.TypeString, Optional: true, Default: "enabled", Description: "Address Resolution Protocol for the interface. disabled - the interface will not use ARP " + "enabled - the interface will use ARP proxy-arp - the interface will use the ARP proxy " + "feature reply-only -the interface will only reply to requests originated from matching " + "IPaddress/MAC address combinations which are entered as static entries inthe '/ip " + "arp' table. No dynamic entries will be automatically stored inthe '/ip arp' table. " + "Therefore for communications to be successful, avalid static entry must already exist.", ValidateFunc: validation.StringInSlice([]string{"disabled", "enabled", "proxy-arp", "reply-only"}, false), }, "arp_interval": { Type: schema.TypeString, Optional: true, Default: "100ms", Description: "Time in milliseconds defines how often to monitor ARP requests.", DiffSuppressFunc: TimeEqual, }, "arp_ip_targets": { Type: schema.TypeString, Optional: true, Description: "IP target address which will be monitored if link-monitoring is set to arp. You can " + "specify multiple IP addresses, separated by a comma.", }, KeyArpTimeout: PropArpTimeoutRw, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "down_delay": { Type: schema.TypeString, Optional: true, Default: "0", Description: "If a link failure has been detected, the bonding interface is disabled for a down-delay " + "time. The value should be a multiple of mii-interval, otherwise, it will be rounded down " + "to the nearest value. This property only has an effect when link-monitoring is set to mii.", DiffSuppressFunc: TimeEqual, }, "forced_mac_address": { Type: schema.TypeString, Optional: true, Description: "Bydefault, the bonding interface will use the MAC address of the firstselected slave " + "interface. This property allows to configure static MACaddress for the bond interface " + "(all zeros, broadcast or multicastaddresses will not apply). RouterOS will " + "automatically change the MACaddress for slave interfaces and it will be visible in " + "/interface ethernet configuration export.", }, "lacp_mode": { Type: schema.TypeString, Optional: true, Description: "Specifies whether ports actively or passively participates in the LACP:" + "\n - **active** - ports actively initiate LACP communication, regardless of the partner's LACP mode " + "(i.e, it \"speaks\" even if the partner is silent)" + "\n - **passive** - ports only respond to LACP messages and do not initiate them unless the partner " + "is in active mode (i.e., it \"listens\" and responds only if spoken to).", ValidateFunc: validation.StringInSlice([]string{"active", "passive"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "lacp_rate": { Type: schema.TypeString, Optional: true, Default: "30secs", Description: "LinkAggregation Control Protocol rate specifies how often to exchange withLACPDUs " + "between bonding peers. Used to determine whether a link is up orother changes have " + "occurred in the network. LACP tries to adapt tothese changes providing failover.", ValidateFunc: validation.StringInSlice([]string{"1sec", "30secs"}, false), }, "lacp_user_key": { Type: schema.TypeInt, Optional: true, Description: "Specifiesthe upper 10 bits of the port key. The lower 6 bits are automatically" + "assigned based on individual port link speed and duplex. The setting isavailable only " + "since RouterOS v7.3.", ValidateFunc: validation.IntBetween(0, 1023), }, "link_monitoring": { Type: schema.TypeString, Optional: true, Default: "mii", Description: "Method to use for monitoring the link (whether it is up or down) arp - uses Address " + "Resolution Protocol to determine whether the remote interface is reachable mii - uses " + "Media Independent Interface to determine link status. Link status determination relies " + "on the device driver. none - no method for link monitoring is used. Note: some bonding " + "modes require specific link monitoring to work properly.", ValidateFunc: validation.StringInSlice([]string{"arp", "mii", "none"}, false), }, KeyMacAddress: PropMacAddressRo, "min_links": { Type: schema.TypeInt, Optional: true, Description: "How many active slave links needed for bonding to become active.", ValidateFunc: validation.IntAtLeast(0), }, "mii_interval": { Type: schema.TypeString, Optional: true, Default: "100ms", Description: "How often to monitor the link for failures (the parameter used only if link-monitoring " + "is mii)", DiffSuppressFunc: TimeEqual, }, "mlag_id": { Type: schema.TypeInt, Optional: true, Description: "ChangesMLAG ID for bonding interface. The same MLAG ID should be used on bothpeer " + "devices to successfully create a single MLAG. See more details on MLAG .", ValidateFunc: validation.IntAtLeast(0), }, "mode": { Type: schema.TypeString, Optional: true, Default: "balance-rr", Description: "Specifies one of the bonding policies:\n * 802.3ad -IEEE 802.3ad dynamic link aggregation. " + "In this mode, the interfaces areaggregated in a group where each slave shares the same " + "speed. Itprovides fault tolerance and load balancing. Slave selection foroutgoing " + "traffic is done according to the transmit-hash-policy\n * active-backup - provides " + "link backup. Only one slave can be active at a time. Another slave only becomes active, " + "if the first one fails.\n * balance-alb - adaptive load balancing. The same as " + "balance-tlb but received traffic is also balanced. The device driver should have support " + "for changing it's MAC address.\n * balance-rr -round-robin load balancing. Slaves in " + "a bonding interface will transmitand receive data in sequential order. It provides " + "load balancing andfault tolerance.\n * balance-tlb -Outgoing traffic is " + "distributed according to the current load on eachslave. Incoming traffic is not " + "balanced and is received by the currentslave. If receiving slave fails, then another " + "slave takes the MACaddress of the failed slave.\n * balance-xor - Transmit based on " + "the selected transmit-hash-policy. This mode provides load balancing and fault " + "tolerance.\n * broadcast -Broadcasts the same data on all interfaces at once. This " + "provides faulttolerance but slows down traffic throughput on some slow machines.", ValidateFunc: validation.StringInSlice( []string{ "802.3ad", "active-backup", "balance-alb", "balance-rr", "balance-tlb", "balance-xor", "broadcast", }, false, ), }, KeyMtu: { Type: schema.TypeInt, Optional: true, Default: 1500, Description: "MaximumTransmit Unit in bytes. Must be smaller or equal to the smallest L2MTUvalue " + "of a bonding slave. L2MTU of a bonding interface is determined bythe lowest L2MTU " + "value among its slave interfaces.", ValidateFunc: validation.IntBetween(64, 65535), }, KeyName: PropName("Name of the bonding interface."), "primary": { Type: schema.TypeString, Optional: true, Default: "none", Description: "Controlsthe primary interface between active slave ports, works only for" + "active-backup, balance-tlb and balance-alb modes. For active-backupmode, it controls " + "which running interface is supposed to send andreceive the traffic. For balance-tlb " + "mode, it controls which runninginterface is supposed to receive all the traffic, but " + "for balance-albmode, it controls which interface is supposed to receive the unbalanced " + " traffic (the non-IPv4 traffic). When none of the interfaces are selectedas primary, " + "device will automatically select the interface that isconfigured as the first one.", }, KeyRunning: PropRunningRo, "slaves": { Type: schema.TypeSet, Required: true, Description: "At least two ethernet-like interfaces separated by a comma, which will be used for " + "bonding", Elem: &schema.Schema{ Type: schema.TypeString, }, }, "up_delay": { Type: schema.TypeString, Optional: true, Default: "0", Description: "If a link has been brought up, the bonding interface is disabled for up-delay time and " + "after this time it is enabled. The value should be a multiple of mii-interval , " + "otherwise, it will be rounded down to the nearest value. This property only has an " + "effect when link-monitoring is set to mii.", DiffSuppressFunc: TimeEqual, }, "transmit_hash_policy": { Type: schema.TypeString, Optional: true, Default: "layer-2", Description: "Selects the transmit hash policy to use for slave selection in balance-xor and 802.3ad " + "modes:\n * layer-2 -Uses XOR of hardware MAC addresses to generate the hash. This algorithm " + " will place all traffic to a particular network peer on the same slave.This algorithm " + "is 802.3ad compliant.\n * layer-2-and-3 -This policy uses a combination of layer2 and " + "layer3 protocolinformation to generate the hash. Uses XOR of hardware MAC addresses " + "andIP addresses to generate the hash. This algorithm will place alltraffic to a " + "particular network peer on the same slave. For non-IPtraffic, the formula is the same " + "as for the layer2 transmit hash policy.This policy is intended to provide a more " + "balanced distribution oftraffic than layer2 alone, especially in environments where a " + "layer3gateway device is required to reach most destinations. This algorithm is" + "802.3ad compliant.\n * layer-3-and-4 - This policyuses upper layer protocol information, " + "when available, to generate thehash. This allows for traffic to a particular network " + "peer to spanmultiple slaves, although a single connection will not span multiple" + "slaves. For fragmented TCP or UDP packets and all other IP protocoltraffic, the source " + "and destination port information is omitted. Fornon-IP traffic, the formula is the " + "same as for the layer2 transmit hashpolicy. This algorithm is not fully 802.3ad " + "compliant.", ValidateFunc: validation.StringInSlice([]string{"layer-2", "layer-2-and-3", "layer-3-and-4"}, false), }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_bonding_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterfaceBondingAddress = "routeros_interface_bonding.test" func TestAccInterfaceBondingTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/interface/bonding", "routeros_interface_bonding"), Steps: []resource.TestStep{ { Config: testAccInterfaceBondingConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceBondingAddress), resource.TestCheckResourceAttr(testInterfaceBondingAddress, "name", "bonding-test"), ), }, }, }) }) } } func testAccInterfaceBondingConfig() string { return providerConfig + ` resource "routeros_interface_bonding" "test" { name = "bonding-test" slaves = ["ether3", "ether4"] } ` } ================================================ FILE: routeros/resource_interface_bridge.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) // ResourceInterfaceBridge https://wiki.mikrotik.com/wiki/Manual:Interface/Bridge func ResourceInterfaceBridge() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/bridge"), MetaId: PropId(Id), KeyActualMtu: PropActualMtuRo, "add_dhcp_option82": { Type: schema.TypeBool, Optional: true, Description: "Whether to add DHCP Option-82 information (Agent Remote ID and Agent Circuit ID) to DHCP " + "packets. Can be used together with Option-82 capable DHCP server to assign IP addresses and implement " + "policies. This property only has effect when dhcp-snooping is set to yes.", RequiredWith: []string{"dhcp_snooping"}, }, "admin_mac": { Type: schema.TypeString, Computed: true, Optional: true, Description: "Static MAC address of the bridge. This property only has effect when auto-mac is set to no.", }, "ageing_time": { Type: schema.TypeString, Optional: true, Description: "How long a host's information will be kept in the bridge database.", DiffSuppressFunc: TimeEqual, }, KeyArp: PropArpRw, KeyArpTimeout: PropArpTimeoutRw, "auto_mac": { Type: schema.TypeBool, Optional: true, Description: "Automatically select one MAC address of bridge ports as a bridge MAC address, bridge MAC " + "will be chosen from the first added bridge port. After a device reboot, the bridge MAC " + "can change depending on the port-number.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyComment: PropCommentRw, "dhcp_snooping": { Type: schema.TypeBool, Optional: true, }, KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, "ether_type": { Type: schema.TypeString, Optional: true, Description: "This property only has effect when vlan-filtering is set to yes.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.StringInSlice([]string{"0x9100", "0x8100", "0x88a8"}, false), }, "fast_forward": { Type: schema.TypeBool, Optional: true, Computed: true, }, "forward_delay": { Type: schema.TypeString, Optional: true, Description: "Time which is spent during the initialization phase of the bridge interface " + "(i.e., after router startup or enabling the interface) in listening/learning state before the " + "bridge will start functioning normally.", DiffSuppressFunc: TimeEqual, }, "forward_reserved_addresses": { Type: schema.TypeBool, Optional: true, Description: "An option whether to forward IEEE reserved multicast MAC addresses that are in the `01:80:C2:00:00:0x` range. This option is available in RouterOS starting from version 7.16.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "frame_types": { Type: schema.TypeString, Optional: true, Description: "Specifies allowed frame types on a bridge port. This property only has effect when " + "vlan-filtering is set to yes.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.StringInSlice([]string{"admit-all", "admit-only-untagged-and-priority-tagged", "admit-only-vlan-tagged"}, false), }, "igmp_snooping": { Type: schema.TypeBool, Optional: true, Description: "Enables multicast group and port learning to prevent multicast traffic from flooding all " + "interfaces in a bridge.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "igmp_version": { Type: schema.TypeInt, Optional: true, Computed: true, Description: "Selects the IGMP version in which IGMP general membership queries will be generated. " + "This property only has effect when igmp-snooping is set to yes.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.IntInSlice([]int{2, 3}), RequiredWith: []string{"igmp_snooping"}, }, "ingress_filtering": { Type: schema.TypeBool, Optional: true, Description: "Enables or disables VLAN ingress filtering, which checks if the ingress port is a member " + "of the received VLAN ID in the bridge VLAN table. Should be used with frame-types to specify if " + "the ingress traffic should be tagged or untagged. This property only has effect when " + "vlan-filtering is set to yes.", DiffSuppressFunc: AlwaysPresentNotUserProvided, RequiredWith: []string{"vlan_filtering"}, }, KeyL2Mtu: PropL2MtuRo, "last_member_interval": { Type: schema.TypeString, Optional: true, Computed: true, Description: "If a port has fast-leave set to no and a bridge port receives a IGMP Leave message, " + "then a IGMP Snooping enabled bridge will send a IGMP query to make sure that no devices has " + "subscribed to a certain multicast stream on a bridge port.", DiffSuppressFunc: TimeEqual, RequiredWith: []string{"igmp_snooping"}, }, "last_member_query_count": { Type: schema.TypeInt, Optional: true, Computed: true, Description: "How many times should last-member-interval pass until a IGMP Snooping bridge will stop " + "forwarding a certain multicast stream. This property only has effect when igmp-snooping is set to yes.", DiffSuppressFunc: AlwaysPresentNotUserProvided, RequiredWith: []string{"igmp_snooping"}, }, KeyMacAddress: PropMacAddressRo, "max_hops": { Type: schema.TypeInt, Optional: true, Computed: true, Description: "Bridge count which BPDU can pass in a MSTP enabled network in the same region before BPDU " + "is being ignored. This property only has effect when protocol-mode is set to mstp.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.IntBetween(6, 40), }, "max_learned_entries": { Type: schema.TypeString, Optional: true, Description: "An option to set the maximum number of learned hosts for the bridge interface. This option is available in RouterOS starting from version 7.16.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "max_message_age": { Type: schema.TypeString, Optional: true, Description: "Changes the Max Age value in BPDU packets, which is transmitted by the root bridge. " + "This property only has effect when protocol-mode is set to stp or rstp. Value: 6s..40s", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "membership_interval": { Type: schema.TypeString, Optional: true, Computed: true, Description: "Amount of time after an entry in the Multicast Database (MDB) is removed if a IGMP membership " + "report is not received on a certain port. This property only has effect when igmp-snooping is set to yes.", DiffSuppressFunc: TimeEqual, RequiredWith: []string{"igmp_snooping"}, }, "mld_version": { Type: schema.TypeInt, Optional: true, Computed: true, Description: "Selects the MLD version. Version 2 adds support for source-specific multicast. This " + "property only has effect when RouterOS IPv6 package is enabled and igmp-snooping is set to yes.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.IntInSlice([]int{1, 2}), RequiredWith: []string{"igmp_snooping"}, }, "mtu": { Type: schema.TypeString, Optional: true, Description: "The default bridge MTU value without any bridge ports added is 1500. " + "The MTU value can be set manually, but it cannot exceed the bridge L2MTU or the lowest bridge " + "port L2MTU. If a new bridge port is added with L2MTU which is smaller than the actual-mtu " + "of the bridge (set by the mtu property), then manually set value will be ignored and the bridge " + "will act as if mtu=auto is set.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "multicast_querier": { Type: schema.TypeBool, Optional: true, Description: "Multicast querier generates IGMP general membership queries to which all IGMP capable " + "devices respond with an IGMP membership report, usually a PIM (multicast) router or IGMP proxy " + "generates these queries. This property only has an effect when igmp-snooping is set to yes. " + "Additionally, the igmp-snooping should be disabled/enabled after changing multicast-querier property.", RequiredWith: []string{"igmp_snooping"}, }, // https://help.mikrotik.com/docs/pages/viewpage.action?pageId=59277403#BridgeIGMP/MLDsnooping-Configurationoptions "multicast_router": { Type: schema.TypeString, Optional: true, Computed: true, Description: "A multicast router port is a port where a multicast router or querier is connected. On " + "this port, unregistered multicast streams and IGMP/MLD membership reports will be sent. This " + "setting changes the state of the multicast router for a bridge interface itself. This property can " + "be used to send IGMP/MLD membership reports and multicast traffic to the bridge interface for further " + "multicast routing or proxying. This property only has an effect when igmp-snooping is set to yes.", ValidateFunc: validation.StringInSlice([]string{"disabled", "permanent", "temporary-query"}, false), RequiredWith: []string{"igmp_snooping"}, }, "mvrp": { Type: schema.TypeBool, Optional: true, //Default: false, Description: "Enables MVRP for bridge (available since RouterOS 7.15). It ensures that the MAC address 01:80:C2:00:00:21 is trapped and not forwarded, the vlan-filtering must be enabled.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyName: PropNameForceNewRw, "port_cost_mode": { Type: schema.TypeString, Optional: true, Description: "An option that changes the port path cost and internal path cost mode for bridged ports, utilizing automatic values based on interface speed.", ValidateFunc: validation.StringInSlice([]string{"long", "short"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "priority": { Type: schema.TypeString, Optional: true, Description: "Bridge priority, used by STP to determine root bridge, used by MSTP to determine CIST " + "and IST regional root bridge. This property has no effect when protocol-mode is set to none.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "protocol_mode": { Type: schema.TypeString, Optional: true, Description: "Select Spanning tree protocol (STP) or Rapid spanning tree protocol (RSTP) to ensure a " + "loop-free topology for any bridged LAN.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.StringInSlice([]string{"none", "rstp", "stp", "mstp"}, false), }, "pvid": { Type: schema.TypeInt, Optional: true, Description: "Port VLAN ID (pvid) specifies which VLAN the untagged ingress traffic is assigned to. " + "It applies e.g. to frames sent from bridge IP and destined to a bridge port. " + "This property only has effect when vlan-filtering is set to yes.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.IntBetween(1, 4094), }, "querier_interval": { Type: schema.TypeString, Optional: true, Computed: true, Description: "Used to change the interval how often a bridge checks if it is the active multicast " + "querier. This property only has effect when igmp-snooping and multicast-querier is set to yes.", DiffSuppressFunc: TimeEqual, RequiredWith: []string{"igmp_snooping", "multicast_querier"}, }, "query_interval": { Type: schema.TypeString, Optional: true, Computed: true, Description: "Used to change the interval how often IGMP general membership queries are sent out. " + "This property only has effect when igmp-snooping and multicast-querier is set to yes.", DiffSuppressFunc: TimeEqual, RequiredWith: []string{"igmp_snooping", "multicast_querier"}, }, "query_response_interval": { Type: schema.TypeString, Optional: true, Computed: true, Description: "Interval in which a IGMP capable device must reply to a IGMP query with a IGMP membership " + "report. This property only has effect when igmp-snooping and multicast-querier is set to yes.", DiffSuppressFunc: TimeEqual, RequiredWith: []string{"igmp_snooping", "multicast_querier"}, }, KeyRunning: PropRunningRo, "region_name": { Type: schema.TypeString, Optional: true, Description: "MSTP region name. This property only has effect when protocol-mode is set to mstp.", }, "region_revision": { Type: schema.TypeInt, Optional: true, Description: "MSTP configuration revision number. This property only has effect when protocol-mode is set to mstp.", ValidateFunc: Validation64k, }, "startup_query_count": { Type: schema.TypeInt, Optional: true, Computed: true, Description: "Specifies how many times must startup-query-interval pass until the bridge starts sending " + "out IGMP general membership queries periodically. This property only has effect when igmp-snooping " + "and multicast-querier is set to yes.", DiffSuppressFunc: AlwaysPresentNotUserProvided, RequiredWith: []string{"igmp_snooping", "multicast_querier"}, }, "startup_query_interval": { Type: schema.TypeString, Optional: true, Computed: true, Description: "Used to change the amount of time after a bridge starts sending out IGMP general membership " + "queries after the bridge is enabled. This property only has effect when igmp-snooping and " + "multicast-querier is set to yes.", DiffSuppressFunc: TimeEqual, RequiredWith: []string{"igmp_snooping", "multicast_querier"}, }, "transmit_hold_count": { Type: schema.TypeInt, Optional: true, Description: "The Transmit Hold Count used by the Port Transmit state machine to limit transmission rate.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.IntBetween(1, 10), }, "vlan_filtering": { Type: schema.TypeBool, Optional: true, Description: "Globally enables or disables VLAN functionality for bridge.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, // Some properties are not implemented, see: https://wiki.mikrotik.com/wiki/Manual:Interface/Bridge } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, SchemaVersion: 1, StateUpgraders: []schema.StateUpgrader{ { Type: ResourceInterfaceBridgeV0().CoreConfigSchema().ImpliedType(), Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), Version: 0, }, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_bridge_filter.go ================================================ package routeros import ( "context" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*3C", "action": "drop", "arp-dst-mac-address": "00:00:00:00:00:00/FF:FF:FF:FF:FF:FF", "arp-gratuitous": "false", "arp-hardware-type": "0", "arp-opcode": "0", "arp-packet-type": "0", "arp-src-mac-address": "00:00:00:00:00:00/FF:FF:FF:FF:FF:FF", "bytes": "0", "chain": "vpn_to_hsia_bridge", "comment": "Explicit block", "disabled": "false", "dst-mac-address": "00:00:00:00:00:00/FF:FF:FF:FF:FF:FF", "dynamic": "false", "in-bridge": "bridge_cast", "in-bridge-list": "dynamic", "ingress-priority": "0", "invalid": "false", "limit": "1,5", "log": "true", "log-prefix": "HSIA -> VPN Blocked ", "mac-protocol": "arp", "out-bridge": "bridge_cast", "out-bridge-list": "static", "packet-mark": "mdns_to_be_dropped", "packet-type": "broadcast", "packets": "0", "src-mac-address": "00:00:00:00:00:00/FF:FF:FF:FF:FF:FF" } */ // ResourceIPFirewallFilter https://help.mikrotik.com/docs/spaces/ROS/pages/328068/Bridging+and+Switching#BridgingandSwitching-BridgeFirewall func ResourceInterfaceBridgeFilter() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/bridge/filter"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("bytes", "packets", "invalid"), MetaSetUnsetFields: PropSetUnsetFields("arp_dst_mac_address", "arp_gratuitous", "arp_hardware_type", "arp_opcode", "arp_packet_type", "arp_src_address", "arp_src_mac_address", "dst_address", "dst_mac_address", "dst_port", "in_bridge", "in_bridge_list", "in_interface", "in_interface_list", "ingress_priority", "ip_protocol", "limit", "mac_protocol", "new_packet_mark", "new_priority", "out_bridge", "out_bridge_list", "out_interface", "out_interface_list", "packet_mark", "packet_type", "src_address", "src_mac_address", "src_port", "stp_flags", "stp_forward_delay", "stp_hello_time", "stp_max_age", "stp_root_address", "stp_port", "stp_root_cost", "stp_root_priority", "stp_sender_address", "stp_sender_priority", "stp_type", "tls_host", "vlan_encap", "vlan_id", "vlan_priority"), "action": { Type: schema.TypeString, Required: true, Description: "Action to take if a packet is matched by the rule", ValidateFunc: validation.StringInSlice([]string{ "accept", "drop", "mark-packet", "jump", "log", "passthrough", "set-priority", "return", }, false), }, "arp_dst_mac_address": { Type: schema.TypeString, Optional: true, Description: "ARP destination MAC address", ValidateFunc: ValidationMacAddress, }, "arp_gratuitous": { Type: schema.TypeBool, Optional: true, Description: "Matches ARP gratuitous packets.", }, "arp_hardware_type": { Type: schema.TypeInt, Optional: true, Description: "ARP hardware type. This is normally Ethernet (Type 1).", }, "arp_opcode": { Type: schema.TypeString, Optional: true, Description: "Action to take if a packet is matched by the rule", ValidateFunc: validation.StringInSlice([]string{ "arp-nak", "drarp-error", "rarp-reply", "drarp-request", "inarp-reply", "inarp-request", "reply", "reply-reverse", "request", "request-reverse", }, true), }, "arp_packet_type": { Type: schema.TypeInt, Optional: true, Description: "ARP Packet Type", ValidateFunc: Validation64k, }, "arp_src_address": { Type: schema.TypeString, Optional: true, Description: "ARP source IP address.", ValidateFunc: ValidationIpAddress, }, "arp_src_mac_address": { Type: schema.TypeString, Optional: true, Description: "ARP source MAC address.", ValidateFunc: ValidationMacAddress, }, "chain": { Type: schema.TypeString, Required: true, Description: "Specifies to which chain rule will be added. If the input does not match the name of an " + "already defined chain, a new chain will be created.", }, KeyComment: PropCommentRw, "dst_address": { Type: schema.TypeString, Optional: true, Description: "Destination IP address (only if MAC protocol is set to IP).", ValidateFunc: ValidationIpAddress, }, "dst_mac_address": { Type: schema.TypeString, Optional: true, Description: "Destination MAC address.", ValidateFunc: ValidationMacAddressWithMask, }, "dst_port": { Type: schema.TypeString, Optional: true, Description: "List of destination port numbers or port number ranges.", }, KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, "in_bridge": { Type: schema.TypeString, Optional: true, Description: "Bridge interface through which the packet is coming in.", }, "in_bridge_list": { Type: schema.TypeString, Optional: true, Description: "Set of bridge interfaces defined in interface list. Works the same as in-bridge.", }, "in_interface": { Type: schema.TypeString, Optional: true, Description: "Physical interface (i.e., bridge port) through which the packet is coming in.", }, "in_interface_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as in-interface.", }, "ingress_priority": { Type: schema.TypeInt, Optional: true, Description: "Integer. Matches the priority of an ingress packet. Priority may be derived from VLAN, WMM, DSCP,or MPLS EXP bit.", ValidateFunc: Validation64k, }, "ip_protocol": { Type: schema.TypeString, Optional: true, Description: "IP protocol (only if MAC protocol is set to IPv4)", ValidateFunc: validation.StringInSlice([]string{ "dccp", "egp", "encap", "etherip", "ggp", "gre", "hmp", "icmp", "icmpv6", "idpr-cmtp", "igmp", "ipencap", "ipip", "ipsec-ah", "ipsec-esp", "ipv6", "ipv6-frag", "ipv6-nonxt", "ipv6-opts", "ipv6-route", "iso-tp4", "l2tp", "ospf", "pim", "pup", "rdp", "rspf", "rsvp", "sctp", "st", "tcp", "udp", "udp-lite", "vmtp", "vrrp", "xns-idp", "xtp", }, true), }, "jump_target": { Type: schema.TypeString, Optional: true, Description: "Name of the target chain to jump to. Applicable only if action=jump.", }, "limit": { Type: schema.TypeString, Optional: true, Description: "Matches packets up to a limited rate (packet rate or bit rate). A rule using this matcher " + "will match until this limit is reached. Parameters are written in the following format: " + "rate[/time],burst:mode.", }, "log": { Type: schema.TypeBool, Optional: true, Description: "Add a message to the system log.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "log_prefix": { Type: schema.TypeString, Optional: true, Description: "Adds specified text at the beginning of every log message. Applicable if action=log or " + "log=yes configured.", }, "mac_protocol": { Type: schema.TypeString, Optional: true, Description: "Ethernet payload type (MAC-level protocol). To match protocol type for VLAN encapsulated " + "frames (0x8100 or 0x88a8), a vlan-encap property should be used.", ValidateFunc: validation.StringInSlice([]string{ "802.2", "arp", "homeplug-av", "ip", "ipv6", "ipx", "length", "lldp", "loop-protect", "mpls-multicast", "mpls-unicast", "packing-compr", "packing-simple", "pppoe", "pppoe-discovery", "rarp", "service-vlan", "vlan", }, true), }, "new_packet_mark": { Type: schema.TypeString, Optional: true, Description: "Sets a new packet-mark value.", }, "new_priority": { Type: schema.TypeInt, Optional: true, Description: "Sets a new priority for a packet. This can be the VLAN, WMM or MPLS EXP priority", ValidateFunc: Validation64k, }, "out_bridge": { Type: schema.TypeString, Optional: true, Description: "Bridge interface through which the packet going out.", }, "out_bridge_list": { Type: schema.TypeString, Optional: true, Description: "Set of bridge interfaces defined in interface list. Works the same as out-bridge.", }, "out_interface": { Type: schema.TypeString, Optional: true, Description: "Interface the packet has entered the router.", }, "out_interface_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as out-interface.", }, "packet_mark": { Type: schema.TypeString, Optional: true, Description: "Match packets with a certain packet mark.", }, "packet_type": { Type: schema.TypeString, Optional: true, Description: "Match packets with a certain packet mark.", ValidateFunc: validation.StringInSlice([]string{"broadcast", "host", "multicast", "other-host"}, true), }, "passthrough": { Type: schema.TypeBool, Optional: true, Description: "Whether to let the packet to pass further (like action passthrough) into the " + "filter or not (property only valid some actions).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyPlaceBefore: PropPlaceBefore, "src_address": { Type: schema.TypeString, Optional: true, Description: "Source port number or range (only for TCP or UDP protocols).", ValidateFunc: ValidationIpAddress, }, "src_mac_address": { Type: schema.TypeString, Optional: true, Description: "Source MAC address.", ValidateFunc: ValidationMacAddress, }, "src_port": { Type: schema.TypeString, Optional: true, Description: "List of source port numbers or port number ranges.", }, "stp_flags": { Type: schema.TypeString, Optional: true, Description: "Match packets with a certain packet mark.", ValidateFunc: validation.StringInSlice([]string{"topology-change", "topology-change-ack"}, false), }, "stp_forward_delay": { Type: schema.TypeInt, Optional: true, Description: "Forward delay timer.", ValidateFunc: Validation64k, }, "stp_hello_time": { Type: schema.TypeInt, Optional: true, Description: "STP hello packets time.", ValidateFunc: Validation64k, }, "stp_max_age": { Type: schema.TypeInt, Optional: true, Description: "Maximal STP message age.", ValidateFunc: Validation64k, }, "stp_root_address": { Type: schema.TypeString, Optional: true, Description: "Root bridge MAC address", ValidateFunc: ValidationMacAddress, }, "stp_port": { Type: schema.TypeInt, Optional: true, Description: "STP port identifier.", ValidateFunc: Validation64k, }, "stp_root_cost": { Type: schema.TypeInt, Optional: true, Description: "Root bridge cost.", ValidateFunc: Validation64k, }, "stp_root_priority": { Type: schema.TypeInt, Optional: true, Description: "", ValidateFunc: Validation64k, }, "stp_sender_address": { Type: schema.TypeString, Optional: true, Description: "STP message sender MAC address.", ValidateFunc: ValidationMacAddress, }, "stp_sender_priority": { Type: schema.TypeInt, Optional: true, Description: "STP sender priority.", ValidateFunc: Validation64k, }, "stp_type": { Type: schema.TypeString, Optional: true, Description: "The BPDU type: config - configuration BPDU OR tcn - topology change notification", ValidateFunc: validation.StringInSlice([]string{"config", "tcn"}, false), }, "tls_host": { Type: schema.TypeString, Optional: true, Description: "Allows matching https traffic based on TLS SNI hostname. Accepts GLOB syntax for wildcard matching", }, "vlan_encap": { Type: schema.TypeInt, Optional: true, Description: "Matches the MAC protocol type encapsulated in the VLAN frame.", ValidateFunc: Validation64k, }, KeyVlanId: PropVlanIdRw("Matches the VLAN identifier field.", false), "vlan_priority": { Type: schema.TypeInt, Optional: true, Description: "Matches the VLAN identifier field.", ValidateFunc: validation.IntBetween(0, 7), }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { skip := resSchema[MetaSkipFields].Default.(string) resSchema[MetaSkipFields].Default = skip + `,"place_before"` defer func() { resSchema[MetaSkipFields].Default = skip }() return ResourceUpdate(ctx, resSchema, d, m) }, DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_bridge_filter_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testBridgeFilterRule = "routeros_interface_bridge_filter.rule" func TestAccBridgeFilterTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/interface/bridge/filter", "routeros_interface_bridge_filter"), Steps: []resource.TestStep{ { Config: testAccBridgeFilterConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testBridgeFilterRule), resource.TestCheckResourceAttr(testBridgeFilterRule, "chain", "forward"), resource.TestCheckResourceAttr(testBridgeFilterRule, "action", "drop"), resource.TestCheckResourceAttr(testBridgeFilterRule, "log_prefix", "Blocking MS broadcast"), resource.TestCheckResourceAttr(testBridgeFilterRule, "comment", "HSIA - Block MS broadcast"), resource.TestCheckResourceAttr(testBridgeFilterRule, "ip_protocol", "udp"), resource.TestCheckResourceAttr(testBridgeFilterRule, "dst_port", "135-137"), resource.TestCheckResourceAttr(testBridgeFilterRule, "mac_protocol", "ip"), resource.TestCheckResourceAttr(testBridgeFilterRule, "disabled", "true"), ), }, }, }) }) } } func testAccBridgeFilterConfig() string { return providerConfig + ` resource "routeros_interface_bridge_filter" "rule" { chain = "forward" action = "drop" log_prefix = "Blocking MS broadcast" comment = "HSIA - Block MS broadcast" ip_protocol = "udp" dst_port = "135-137" mac_protocol = "ip" disabled = true } ` } ================================================ FILE: routeros/resource_interface_bridge_mlag _test.go ================================================ package routeros import ( "testing" ) func TestAccInterfaceBridgeMlagTest_basic(t *testing.T) { t.Log("Test skipped, The test is skipped, the resource is only available on real hardware.") } ================================================ FILE: routeros/resource_interface_bridge_mlag.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { "bridge":"bridge1", "peer-port":"stack-link" } */ // https://help.mikrotik.com/docs/display/ROS/Multi-chassis+Link+Aggregation+Group func ResourceInterfaceBridgeMlag() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/bridge/mlag"), MetaId: PropId(Id), "bridge": { Type: schema.TypeString, Required: true, Description: "The bridge interface where MLAG is being created.", }, "heartbeat": { Type: schema.TypeString, Optional: true, Description: "This setting controls how often heartbeat messages are sent to check the connection between peers. " + "If no heartbeat message is received for three intervals in a row, the peer logs a warning about " + "potential communication problems. If set to none, heartbeat messages are not sent at all.", DiffSuppressFunc: TimeEqual, }, "peer_port": { Type: schema.TypeString, Required: true, Description: "An interface that will be used as a peer port. Both peer devices are using inter-chassis " + "communication over these peer ports to establish MLAG and update the host table. Peer port should be " + "isolated on a different untagged VLAN using a pvid setting. Peer port can be configured as a bonding " + "interface.", }, "priority": { Type: schema.TypeInt, Optional: true, Description: "This setting changes the priority for selecting the primary MLAG node. A lower number means " + "higher priority. If both MLAG nodes have the same priority, the one with the lowest bridge MAC address " + "will become the primary device.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_bridge_port.go ================================================ package routeros import ( "fmt" "strconv" "github.com/hashicorp/go-cty/cty" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* [ { ".id": "*0", ".nextid": "*1", "auto-isolate": "false", "bpdu-guard": "false", "bridge": "bridge", "broadcast-flood": "true", "comment": "defconf", "debug-info": " prio 0x0 num 2\n role:Root (1) learn 1 forward 1 infoIs Rcvd edge 0 sendRSTP 1\n proposing 0 agreed 0 agree 1 synced 1 isolate 0 newInfo 0\n migration:SENSING tc:ACTIVE\n ptimes: Msg:0 Max: 5120 FD: 3840 HT: 512\n pprio: RBI: 8000:744D288FCA7D RPC: 0 BI: 8000:744D288FCA7D tP: 0x1 rP: 0x2\n dtimes: Msg:256 Max: 5120 FD: 3840 HT: 512\n dprio: RBI: 8000:744D288FCA7D RPC: 10 BI: 8000:C4AD3407AD79 tP: 0x2 rP: 0x2\n ", "designated-bridge": "0x8000.74:4D:28:8F:CA:7D", "designated-bridge-id": "0x8000.74:4D:28:8F:CA:7D", "designated-cost": "0", "designated-port-id": "0x80.6", "designated-port-number": "1", "disabled": "false", "dynamic": "false", "edge": "auto", "edge-port": "false", "edge-port-discovery": "true", "external-fdb-status": "false", "fast-leave": "false", "forward-transitions": "1", "forwarding": "true", "frame-types": "admit-only-vlan-tagged", "horizon": "none", "hw": "true", "hw-offload": "false", "inactive": "false", "ingress-filtering": "true", "interface": "ether2", "internal-path-cost": "10", "learn": "auto", "learning": "true", "multicast-router": "temporary-query", "path-cost": "10", "point-to-point": "auto", "point-to-point-port": "true", "port-number": "2", "port-id": "0x80.2", "priority": "0x80", "pvid": "1", "restricted-role": "false", "restricted-tcn": "false", "role": "root-port", "rx-bpdu": "0", "rx-tc": "0", "root-path-cost": "10", "sending-rstp": "true", "status": "in-bridge", "tag-stacking": "false", "topology-changes": "0", "trusted": "false", "tx-bpdu": "2157", "tx-tc": "0", "unknown-multicast-flood": "true", "unknown-unicast-flood": "true" }, {...} */ // ResourceInterfaceBridgePort https://wiki.mikrotik.com/wiki/Manual:Interface/Bridge#Port_Settings func ResourceInterfaceBridgePort() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/bridge/port"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("debug_info", "discard_transitions", "forward_transitions", "port_number", "rx_bpdu", "rx_tc", "topology_changes", "tx_bpdu", "tx_tc"), "nextid": { Type: schema.TypeString, Computed: true, }, "auto_isolate": { Type: schema.TypeBool, Optional: true, Description: "When enabled, prevents a port moving from discarding into forwarding state if no BPDUs " + "are received from the neighboring bridge. The port will change into a forwarding state only when " + "a BPDU is received. This property only has an effect when protocol-mode is set to rstp or mstp and " + "edge is set to no.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "bpdu_guard": { Type: schema.TypeBool, Optional: true, Description: "This property has no effect when protocol-mode is set to none.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "bridge": { Type: schema.TypeString, Required: true, }, "broadcast_flood": { Type: schema.TypeBool, Optional: true, Description: "When enabled, bridge floods broadcast traffic to all bridge egress ports. " + "When disabled, drops broadcast traffic on egress ports. ", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyComment: PropCommentRw, "designated_bridge": { Type: schema.TypeString, Computed: true, Description: "Root bridge ID (bridge priority and the bridge MAC address).", }, "designated_bridge_id": { Type: schema.TypeString, Computed: true, Description: "Shows the designated bridge identifier, as determined from the port's priority vector.", }, "designated_cost": { Type: schema.TypeString, Computed: true, Description: "Designated cost.", }, "designated_port_id": { Type: schema.TypeString, Computed: true, Description: "Shows the designated port identifier, as determined from the port's priority vector.", }, "designated_port_number": { Type: schema.TypeInt, Computed: true, Description: "Designated port number.", }, KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, "edge": { Type: schema.TypeString, Optional: true, Description: "Set port as edge port or non-edge port, or enable edge discovery. " + "Edge ports are connected to a LAN that has no other bridges attached. ", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.StringInSlice([]string{"auto", "no", "no-discover", "yes", "yes-discover"}, false), }, "edge_port": { Type: schema.TypeBool, Computed: true, Description: "Whether port is an edge port or not.", }, "edge_port_discovery": { Type: schema.TypeBool, Computed: true, Description: "Whether port is set to automatically detect edge ports.", }, // Where external-fdb (auto | no | yes; Default: auto) ??? "external_fdb_status": { Type: schema.TypeBool, Computed: true, Description: "Whether registration table is used instead of forwarding data base.", }, "fast_leave": { Type: schema.TypeBool, Optional: true, Description: "Enables IGMP Fast leave feature on the port.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "forwarding": { Type: schema.TypeBool, Computed: true, Description: "Shows if the port is not blocked by (R/M)STP.", }, "frame_types": { Type: schema.TypeString, Optional: true, Description: "Specifies allowed ingress frame types on a bridge port. " + "This property only has effect when vlan-filtering is set to yes.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.StringInSlice([]string{"admit-all", "admit-only-untagged-and-priority-tagged", "admit-only-vlan-tagged"}, false), }, "horizon": { Type: schema.TypeString, Optional: true, Description: "Use split horizon bridging to prevent bridging loops. Set the same value for group of ports, " + "to prevent them from sending data to ports with the same horizon value. Split horizon is a software " + "feature that disables hardware offloading. This value is integer '0'..'429496729' or 'none'.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "hw": { Type: schema.TypeBool, Computed: true, Optional: true, Description: "Enable or disable Hardware Offloading of the interface.", }, "hw_offload": { Type: schema.TypeBool, Computed: true, Description: "Hardware offloading state.", }, "hw_offload_group": { Type: schema.TypeString, Computed: true, Description: "Switch chip used by the port.", }, KeyInactive: PropInactiveRo, "ingress_filtering": { Type: schema.TypeBool, Optional: true, Computed: true, Description: "Enables or disables VLAN ingress filtering, which checks if the ingress port is a member of " + "the received VLAN ID in the bridge VLAN table. Should be used with frame-types to specify if the " + "ingress traffic should be tagged or untagged. This property only has effect when vlan-filtering " + "is set to yes.", }, KeyInterface: PropInterfaceRw, "internal_path_cost": { Type: schema.TypeInt, Optional: true, Description: "Path cost to the interface for MSTI0 inside a region. This property only has effect when " + "protocol-mode is set to mstp.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "last_topology_change": { Type: schema.TypeString, Computed: true, Description: "Last topology change timer, records time since the last change.", }, "learn": { Type: schema.TypeString, Optional: true, Description: "Changes MAC learning behaviour on a bridge port ", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: ValidationAutoYesNo, }, "learning": { Type: schema.TypeBool, Computed: true, Description: "Shows whether the port is capable of learning MAC addresses.", }, "multicast_router": { Type: schema.TypeString, Optional: true, Description: "Changes the state of a bridge port whether IGMP membership reports are going to be " + "forwarded to this port.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.StringInSlice([]string{"disabled", "permanent", "temporary-query"}, false), }, "mvrp_applicant_state": { Type: schema.TypeString, Optional: true, //Default: "normal-participant", Description: "MVRP applicant options (available since RouterOS 7.15): " + "- non-participant - port does not send any MRP messages; " + "- normal-participant - port participates normally in MRP exchanges.", ValidateFunc: validation.StringInSlice([]string{"non-participant", "normal-participant"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "mvrp_registrar_state": { Type: schema.TypeString, Optional: true, //Default: "normal", Description: "MVRP registrar options (available since RouterOS 7.15): " + "- fixed - port ignores all MRP messages, and remains Registered (IN) in all configured vlans. " + "- normal - port receives MRP messages and handles them according to the standard.", ValidateFunc: validation.StringInSlice([]string{"fixed", "normal"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, // This field has a string value because on the x86 architecture there is no good way to validate // values up to 4294967295. And in this case, an overflow occurs with an errors: // "Cannot use 4294967295 (untyped int constant) as int value in argument to validation.IntBetween (overflows)" // or "Attribute must be a whole number, got 4.294967295e+09". "path_cost": { Type: schema.TypeString, Optional: true, Description: `Path cost to the interface, used by STP to determine the "best" path, used by MSTP to` + `determine "best" path between regions. This property has no effect when protocol-mode is set to none.`, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "point_to_point": { Type: schema.TypeString, Optional: true, Description: "Specifies if a bridge port is connected to a bridge using a point-to-point link for faster " + "convergence in case of failure. This property has no effect when protocol-mode is set to none.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: ValidationAutoYesNo, }, "point_to_point_port": { Type: schema.TypeBool, Computed: true, Description: "Whether the port is connected to a bridge port using full-duplex (true) or half-duplex (false).", }, "port_id": { Type: schema.TypeString, Computed: true, Description: "In Spanning Tree Protocol each port has a unique Port Identifier. Priority[hex] + port number.", }, "priority": { Type: schema.TypeString, Optional: true, Description: "The priority of the interface, used by STP to determine the root port, " + "used by MSTP to determine root port between regions.", // ValidateFunc: validation.IntBetween(0, 240), ValidateDiagFunc: func(i interface{}, p cty.Path) diag.Diagnostics { min, max := 0, 240 v, ok := i.(string) if !ok { return diag.Diagnostics{ diag.Diagnostic{ Severity: diag.Error, Summary: "bad value type", Detail: fmt.Sprintf("Value should be a string: %v (type = %T)", v, v), }, } } value, err := strconv.ParseInt(v, 0, 64) if err != nil { return diag.Diagnostics{ diag.Diagnostic{ Severity: diag.Error, Summary: err.Error(), Detail: fmt.Sprintf("Value should be dec or hex: %v", v), }, } } if int(value) < min || int(value) > max { return diag.Diagnostics{ diag.Diagnostic{ Severity: diag.Error, Summary: "value is out of range", Detail: fmt.Sprintf("Expected `priority` to be in the range (%d - %d), got %v", min, max, v), }, } } if int(value)&0xF > 0 { return diag.Diagnostics{ diag.Diagnostic{ Severity: diag.Error, Summary: "wrong priority value", Detail: fmt.Sprintf("Only 4 highest bits can be used in priority, got '%04b %04b'", value&0xFF>>4, value&0xF), }, } } return nil }, DiffSuppressFunc: HexEqual, }, "pvid": { Type: schema.TypeInt, Optional: true, Description: "ort VLAN ID (pvid) specifies which VLAN the untagged ingress traffic is assigned to. " + "This property only has effect when vlan-filtering is set to yes.", ValidateFunc: validation.IntBetween(1, 4096), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "restricted_role": { Type: schema.TypeBool, Optional: true, Description: "Enable the restricted role on a port, used by STP to forbid a port becoming a root port. " + "This property only has effect when protocol-mode is set to mstp.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "restricted_tcn": { Type: schema.TypeBool, Optional: true, Description: "Disable topology change notification (TCN) sending on a port, used by STP to forbid network " + "topology changes to propagate. This property only has effect when protocol-mode is set to mstp.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "role": { Type: schema.TypeString, Computed: true, Description: "(R/M)STP algorithm assigned role of the port", }, // https://wiki.mikrotik.com/wiki/Manual:Interface/Bridge#Bridge_Monitoring "root_path_cost": { Type: schema.TypeInt, Computed: true, Description: "The total cost of the path to the root-bridge.", }, "sending_rstp": { Type: schema.TypeString, Computed: true, Description: "Whether the port is sending RSTP or MSTP BPDU types. A port will transit to STP type " + "when RSTP/MSTP enabled port receives a STP BPDU", }, "status": { Type: schema.TypeString, Computed: true, Description: "Port status ('in-bridge' - port is enabled).", }, "tag_stacking": { Type: schema.TypeBool, Optional: true, Description: "Forces all packets to be treated as untagged packets. Packets on ingress port will be tagged " + "with another VLAN tag regardless if a VLAN tag already exists, packets will be tagged with a VLAN ID " + "that matches the pvid value and will use EtherType that is specified in ether-type. " + "This property only has effect when vlan-filtering is set to yes.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "trusted": { Type: schema.TypeBool, Optional: true, Description: "When enabled, it allows to forward DHCP packets towards DHCP server through this port. " + "Mainly used to limit unauthorized servers to provide malicious information for users. " + "This property only has effect when dhcp-snooping is set to yes.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "unknown_multicast_flood": { Type: schema.TypeBool, Optional: true, Description: "When enabled, bridge floods unknown multicast traffic to all bridge egress ports.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "unknown_unicast_flood": { Type: schema.TypeBool, Optional: true, Description: "When enabled, bridge floods unknown unicast traffic to all bridge egress ports.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_bridge_port_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterfaceBridgePortAddress = "routeros_interface_bridge_port.test_port" func TestAccInterfaceBridgePortTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/interface/bridge/port", "routeros_interface_bridge_port"), Steps: []resource.TestStep{ { Config: testAccInterfaceBridgePortConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceBridgePortAddress), resource.TestCheckResourceAttr(testInterfaceBridgePortAddress, "bridge", "bridge"), ), }, }, }) }) } } func testAccInterfaceBridgePortConfig() string { return providerConfig + ` resource "routeros_interface_bridge_port" "test_port" { bridge = "bridge" interface = "ether1" pvid = 200 disabled = true priority = "80" } ` } ================================================ FILE: routeros/resource_interface_bridge_settings.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { "allow-fast-path": "true", "bridge-fast-forward-bytes": "0", "bridge-fast-forward-packets": "0", "bridge-fast-path-active": "true", "bridge-fast-path-bytes": "0", "bridge-fast-path-packets": "0", "use-ip-firewall": "false", "use-ip-firewall-for-pppoe": "false", "use-ip-firewall-for-vlan": "false" } */ // https://wiki.mikrotik.com/wiki/Manual:Interface/Bridge#Bridge_Settings func ResourceInterfaceBridgeSettings() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/bridge/settings"), MetaId: PropId(Name), "use_ip_firewall": { Type: schema.TypeBool, Optional: true, Default: false, Description: "Force bridged traffic to also be processed by prerouting, forward and postrouting sections " + "of IP routing ( Packet Flow). This does not apply to routed traffic. This property is required in " + "case you want to assign Simple Queues or global Queue Tree to traffic in a bridge. Property " + "use-ip-firewall-for-vlan is required in case bridge vlan-filtering is used.", }, "use_ip_firewall_for_pppoe": { Type: schema.TypeBool, Optional: true, Default: false, Description: "Send bridged un-encrypted PPPoE traffic to also be processed by IP/Firewall. This " + "property only has effect when use-ip-firewall is set to yes. This property is required " + "in case you want to assign Simple Queues or global Queue Tree to PPPoE traffic in a " + "bridge.", }, "use_ip_firewall_for_vlan": { Type: schema.TypeBool, Optional: true, Default: false, Description: "Send bridged VLAN traffic to also be processed by IP/Firewall. This property only has " + "effect when use-ip-firewall is set to yes. This property is required in case you want " + "to assign Simple Queues or global Queue Tree to VLAN traffic in a bridge.", }, "allow_fast_path": { Type: schema.TypeBool, Optional: true, Default: true, Description: "Whether to enable a bridge FastPath globally.", }, "bridge_fast_path_active": { Type: schema.TypeBool, Computed: true, Description: "Shows whether a bridge FastPath is active globally, FastPatch status per bridge " + "interface is not displayed.", }, "bridge_fast_path_packets": { Type: schema.TypeInt, Computed: true, Description: "Shows packet count forwarded by Bridge FastPath.", }, "bridge_fast_path_bytes": { Type: schema.TypeInt, Computed: true, Description: "Shows byte count forwarded by Bridge Fast Path.", }, "bridge_fast_forward_packets": { Type: schema.TypeInt, Computed: true, Description: "Shows packet count forwarded by Bridge Fast Forward.", }, "bridge_fast_forward_bytes": { Type: schema.TypeInt, Computed: true, Description: "Shows byte count forwarded by Bridge Fast Forward.", }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_bridge_settings_test.go ================================================ package routeros import ( "strings" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterfaceBridgeSettingsAddress = "routeros_interface_bridge_settings.test" func TestAccInterfaceBridgeSettingsTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccInterfaceBridgeSettingsConfig(name), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceBridgeSettingsAddress), resource.TestCheckResourceAttr(testInterfaceBridgeSettingsAddress, "id", "interface.bridge.settings"), ), }, }, }) }) } } func testAccInterfaceBridgeSettingsConfig(testName string) string { if strings.Contains(testName, "API") { return providerConfig + ` resource "routeros_interface_bridge_settings" "test" { use_ip_firewall = true } ` } return providerConfig + ` resource "routeros_interface_bridge_settings" "test" { use_ip_firewall = false } ` } ================================================ FILE: routeros/resource_interface_bridge_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterfaceBridgeAddress = "routeros_interface_bridge.test_bridge" const testInterfaceBridgeAddressWithSpace = "routeros_interface_bridge.test_bridge_w_space" func TestAccInterfaceBridgeTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/interface/bridge", "routeros_interface_bridge"), Steps: []resource.TestStep{ { Config: testAccInterfaceBridgeConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceBridgeAddress), resource.TestCheckResourceAttr(testInterfaceBridgeAddress, "name", "test_bridge"), ), }, { Config: testAccInterfaceBridgeConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceBridgeAddressWithSpace), resource.TestCheckResourceAttr(testInterfaceBridgeAddressWithSpace, "name", "Main bridge"), ), }, }, }) }) } } func testAccInterfaceBridgeConfig() string { return providerConfig + ` resource "routeros_interface_bridge" "test_bridge" { name = "test_bridge" } resource "routeros_interface_bridge" "test_bridge_w_space" { name = "Main bridge" ageing_time = "300s" ingress_filtering = true protocol_mode = "rstp" priority = "0x3000" igmp_snooping = true vlan_filtering = true } ` } ================================================ FILE: routeros/resource_interface_bridge_v0.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) func ResourceInterfaceBridgeV0() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/bridge"), MetaId: PropId(Name), KeyActualMtu: PropActualMtuRo, "add_dhcp_option82": { Type: schema.TypeBool, Optional: true, Description: "Whether to add DHCP Option-82 information (Agent Remote ID and Agent Circuit ID) to DHCP " + "packets. Can be used together with Option-82 capable DHCP server to assign IP addresses and implement " + "policies. This property only has effect when dhcp-snooping is set to yes.", RequiredWith: []string{"dhcp_snooping"}, }, "admin_mac": { Type: schema.TypeString, Computed: true, Optional: true, Description: "Static MAC address of the bridge. This property only has effect when auto-mac is set to no.", }, "ageing_time": { Type: schema.TypeString, Optional: true, Default: "5m", Description: "How long a host's information will be kept in the bridge database.", }, KeyArp: PropArpRw, KeyArpTimeout: PropArpTimeoutRw, "auto_mac": { Type: schema.TypeBool, Optional: true, Default: true, Description: "Automatically select one MAC address of bridge ports as a bridge MAC address, bridge MAC " + "will be chosen from the first added bridge port. After a device reboot, the bridge MAC " + "can change depending on the port-number.", }, KeyComment: PropCommentRw, "dhcp_snooping": { Type: schema.TypeBool, Optional: true, }, KeyDisabled: PropDisabledRw, "ether_type": { Type: schema.TypeString, Optional: true, Default: "0x8100", Description: "This property only has effect when vlan-filtering is set to yes.", ValidateFunc: validation.StringInSlice([]string{"0x9100", "0x8100", "0x88a8"}, false), }, "fast_forward": { Type: schema.TypeBool, Optional: true, Computed: true, }, "forward_delay": { Type: schema.TypeString, Optional: true, Default: "15s", Description: "Time which is spent during the initialization phase of the bridge interface " + "(i.e., after router startup or enabling the interface) in listening/learning state before the " + "bridge will start functioning normally.", }, "frame_types": { Type: schema.TypeString, Optional: true, Default: "admit-all", Description: "Specifies allowed frame types on a bridge port. This property only has effect when " + "vlan-filtering is set to yes.", ValidateFunc: validation.StringInSlice([]string{"admit-all", "admit-only-untagged-and-priority-tagged", "admit-only-vlan-tagged"}, false), }, "igmp_snooping": { Type: schema.TypeBool, Optional: true, Default: false, Description: "Enables multicast group and port learning to prevent multicast traffic from flooding all " + "interfaces in a bridge.", }, "igmp_version": { Type: schema.TypeInt, Optional: true, // Default: "2", Computed: true, Description: "Selects the IGMP version in which IGMP general membership queries will be generated. " + "This property only has effect when igmp-snooping is set to yes.", ValidateFunc: validation.IntInSlice([]int{2, 3}), RequiredWith: []string{"igmp_snooping"}, }, "ingress_filtering": { Type: schema.TypeBool, Optional: true, Default: false, Description: "Enables or disables VLAN ingress filtering, which checks if the ingress port is a member " + "of the received VLAN ID in the bridge VLAN table. Should be used with frame-types to specify if " + "the ingress traffic should be tagged or untagged. This property only has effect when " + "vlan-filtering is set to yes.", RequiredWith: []string{"vlan_filtering"}, }, KeyL2Mtu: PropL2MtuRo, "last_member_interval": { Type: schema.TypeString, Optional: true, Computed: true, Description: "If a port has fast-leave set to no and a bridge port receives a IGMP Leave message, " + "then a IGMP Snooping enabled bridge will send a IGMP query to make sure that no devices has " + "subscribed to a certain multicast stream on a bridge port.", DiffSuppressFunc: TimeEqual, RequiredWith: []string{"igmp_snooping"}, }, "last_member_query_count": { Type: schema.TypeInt, Optional: true, // Default: 2, Computed: true, Description: "How many times should last-member-interval pass until a IGMP Snooping bridge will stop " + "forwarding a certain multicast stream. This property only has effect when igmp-snooping is set to yes.", RequiredWith: []string{"igmp_snooping"}, }, KeyMacAddress: PropMacAddressRo, "max_hops": { Type: schema.TypeInt, Optional: true, // Default: 20, Computed: true, Description: "Bridge count which BPDU can pass in a MSTP enabled network in the same region before BPDU " + "is being ignored. This property only has effect when protocol-mode is set to mstp.", ValidateFunc: validation.IntBetween(6, 40), }, "max_message_age": { Type: schema.TypeString, Optional: true, Default: "20s", Description: "Changes the Max Age value in BPDU packets, which is transmitted by the root bridge. " + "This property only has effect when protocol-mode is set to stp or rstp. Value: 6s..40s", }, "membership_interval": { Type: schema.TypeString, Optional: true, // Default: "4m20s", Computed: true, Description: "Amount of time after an entry in the Multicast Database (MDB) is removed if a IGMP membership " + "report is not received on a certain port. This property only has effect when igmp-snooping is set to yes.", DiffSuppressFunc: TimeEqual, RequiredWith: []string{"igmp_snooping"}, }, "mld_version": { Type: schema.TypeInt, Optional: true, // Default: 1, Computed: true, Description: "Selects the MLD version. Version 2 adds support for source-specific multicast. This " + "property only has effect when RouterOS IPv6 package is enabled and igmp-snooping is set to yes.", ValidateFunc: validation.IntInSlice([]int{1, 2}), RequiredWith: []string{"igmp_snooping"}, }, "mtu": { Type: schema.TypeString, Optional: true, Default: "auto", Description: "The default bridge MTU value without any bridge ports added is 1500. " + "The MTU value can be set manually, but it cannot exceed the bridge L2MTU or the lowest bridge " + "port L2MTU. If a new bridge port is added with L2MTU which is smaller than the actual-mtu " + "of the bridge (set by the mtu property), then manually set value will be ignored and the bridge " + "will act as if mtu=auto is set.", }, "multicast_querier": { Type: schema.TypeBool, Optional: true, Description: "Multicast querier generates IGMP general membership queries to which all IGMP capable " + "devices respond with an IGMP membership report, usually a PIM (multicast) router or IGMP proxy " + "generates these queries. This property only has an effect when igmp-snooping is set to yes. " + "Additionally, the igmp-snooping should be disabled/enabled after changing multicast-querier property.", RequiredWith: []string{"igmp_snooping"}, }, // https://help.mikrotik.com/docs/pages/viewpage.action?pageId=59277403#BridgeIGMP/MLDsnooping-Configurationoptions "multicast_router": { Type: schema.TypeString, Optional: true, Computed: true, Description: "A multicast router port is a port where a multicast router or querier is connected. On " + "this port, unregistered multicast streams and IGMP/MLD membership reports will be sent. This " + "setting changes the state of the multicast router for a bridge interface itself. This property can " + "be used to send IGMP/MLD membership reports and multicast traffic to the bridge interface for further " + "multicast routing or proxying. This property only has an effect when igmp-snooping is set to yes.", ValidateFunc: validation.StringInSlice([]string{"disabled", "permanent", "temporary-query"}, false), RequiredWith: []string{"igmp_snooping"}, }, KeyName: PropNameForceNewRw, "priority": { Type: schema.TypeString, Optional: true, Default: "0x8000", Description: "Bridge priority, used by STP to determine root bridge, used by MSTP to determine CIST " + "and IST regional root bridge. This property has no effect when protocol-mode is set to none.", }, "protocol_mode": { Type: schema.TypeString, Optional: true, Default: "rstp", Description: "Select Spanning tree protocol (STP) or Rapid spanning tree protocol (RSTP) to ensure a " + "loop-free topology for any bridged LAN.", ValidateFunc: validation.StringInSlice([]string{"none", "rstp", "stp", "mstp"}, false), }, "pvid": { Type: schema.TypeInt, Optional: true, Default: 1, Description: "Port VLAN ID (pvid) specifies which VLAN the untagged ingress traffic is assigned to. " + "It applies e.g. to frames sent from bridge IP and destined to a bridge port. " + "This property only has effect when vlan-filtering is set to yes.", ValidateFunc: validation.IntBetween(1, 4094), }, "querier_interval": { Type: schema.TypeString, Optional: true, // Default: "4m15s", Computed: true, Description: "Used to change the interval how often a bridge checks if it is the active multicast " + "querier. This property only has effect when igmp-snooping and multicast-querier is set to yes.", DiffSuppressFunc: TimeEqual, RequiredWith: []string{"igmp_snooping", "multicast_querier"}, }, "query_interval": { Type: schema.TypeString, Optional: true, // Default: "2m5s", Computed: true, Description: "Used to change the interval how often IGMP general membership queries are sent out. " + "This property only has effect when igmp-snooping and multicast-querier is set to yes.", DiffSuppressFunc: TimeEqual, RequiredWith: []string{"igmp_snooping", "multicast_querier"}, }, "query_response_interval": { Type: schema.TypeString, Optional: true, // Default: "10s", Computed: true, Description: "Interval in which a IGMP capable device must reply to a IGMP query with a IGMP membership " + "report. This property only has effect when igmp-snooping and multicast-querier is set to yes.", DiffSuppressFunc: TimeEqual, RequiredWith: []string{"igmp_snooping", "multicast_querier"}, }, KeyRunning: PropRunningRo, "region_name": { Type: schema.TypeString, Optional: true, Description: "MSTP region name. This property only has effect when protocol-mode is set to mstp.", }, "region_revision": { Type: schema.TypeInt, Optional: true, Description: "MSTP configuration revision number. This property only has effect when protocol-mode is set to mstp.", ValidateFunc: Validation64k, }, "startup_query_count": { Type: schema.TypeInt, Optional: true, // Default: 2, Computed: true, Description: "Specifies how many times must startup-query-interval pass until the bridge starts sending " + "out IGMP general membership queries periodically. This property only has effect when igmp-snooping " + "and multicast-querier is set to yes.", RequiredWith: []string{"igmp_snooping", "multicast_querier"}, }, "startup_query_interval": { Type: schema.TypeString, Optional: true, // Default: "31s250ms", Computed: true, Description: "Used to change the amount of time after a bridge starts sending out IGMP general membership " + "queries after the bridge is enabled. This property only has effect when igmp-snooping and " + "multicast-querier is set to yes.", DiffSuppressFunc: TimeEqual, RequiredWith: []string{"igmp_snooping", "multicast_querier"}, }, "transmit_hold_count": { Type: schema.TypeInt, Optional: true, Default: 6, Description: "The Transmit Hold Count used by the Port Transmit state machine to limit transmission rate.", ValidateFunc: validation.IntBetween(1, 10), }, "vlan_filtering": { Type: schema.TypeBool, Optional: true, Default: false, Description: "Globally enables or disables VLAN functionality for bridge.", }, // Some properties are not implemented, see: https://wiki.mikrotik.com/wiki/Manual:Interface/Bridge }, } } ================================================ FILE: routeros/resource_interface_bridge_vlan.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* [ { ".id": "*1", "bridge": "bridge", "comment": "Management", "current-tagged": "bridge,ether2,ether3", "current-untagged": "", "disabled": "false", "dynamic": "false", "tagged": "ether2,ether4,ether5,bridge,ether3", "untagged": "", "vlan-ids": "2" }, {...} ] */ // ResourceInterfaceBridgeVlan https://wiki.mikrotik.com/wiki/Manual:Interface/Bridge#Bridge_VLAN_Filtering func ResourceInterfaceBridgeVlan() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/bridge/vlan"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("debug_info"), "bridge": { Type: schema.TypeString, Required: true, Description: "The bridge interface which the respective VLAN entry is intended for.", }, KeyComment: PropCommentRw, "current_tagged": { Type: schema.TypeList, Computed: true, Elem: &schema.Schema{ Type: schema.TypeString, }, }, "current_untagged": { Type: schema.TypeList, Computed: true, Elem: &schema.Schema{ Type: schema.TypeString, }, }, KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, "mvrp_forbidden": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "Ports that ignore all MRP messages and remains Not Registered (MT), as well as disables applicant from declaring specific VLAN ID (available since RouterOS 7.15).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "tagged": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "Interface list with a VLAN tag adding action in egress. This setting accepts comma " + "separated values. E.g. tagged=ether1,ether2.", }, "untagged": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "Interface list with a VLAN tag removing action in egress. This setting accepts comma " + "separated values. E.g. untagged=ether3,ether4", }, "vlan_ids": { Type: schema.TypeSet, Required: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "The list of VLAN IDs for certain port configuration. This setting accepts VLAN ID range " + "as well as comma separated values. E.g. `vlan-ids=[\"100-115\",\"120\",\"122\",\"128-130\"]`.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, SchemaVersion: 1, StateUpgraders: []schema.StateUpgrader{ { Type: ResourceInterfaceBridgeVlanV0().CoreConfigSchema().ImpliedType(), Upgrade: stateMigrationScalarToList("vlan_ids"), Version: 0, }, }, } } ================================================ FILE: routeros/resource_interface_bridge_vlan_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterfaceBridgeVlanAddress = "routeros_interface_bridge_vlan.test_vlan" func TestAccInterfaceBridgeVlanTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/interface/bridge/vlan", "routeros_interface_bridge_vlan"), Steps: []resource.TestStep{ { Config: testAccInterfaceBridgeVlanConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceBridgeVlanAddress), resource.TestCheckResourceAttr(testInterfaceBridgeVlanAddress, "bridge", "bridge"), ), }, }, }) }) } } func testAccInterfaceBridgeVlanConfig() string { return providerConfig + ` resource "routeros_interface_bridge_vlan" "test_vlan" { bridge = "bridge" untagged = ["ether1"] tagged = ["bridge"] vlan_ids = [200] } ` } ================================================ FILE: routeros/resource_interface_bridge_vlan_v0.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* [ { ".id": "*1", "bridge": "bridge", "comment": "Management", "current-tagged": "bridge,ether2,ether3", "current-untagged": "", "disabled": "false", "dynamic": "false", "tagged": "ether2,ether4,ether5,bridge,ether3", "untagged": "", "vlan-ids": "2" }, {...} ] */ // ResourceInterfaceBridgeVlan https://wiki.mikrotik.com/wiki/Manual:Interface/Bridge#Bridge_VLAN_Filtering func ResourceInterfaceBridgeVlanV0() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/bridge/vlan"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("debug_info"), "bridge": { Type: schema.TypeString, Required: true, Description: "The bridge interface which the respective VLAN entry is intended for.", }, KeyComment: PropCommentRw, "current_tagged": { Type: schema.TypeList, Computed: true, Elem: &schema.Schema{ Type: schema.TypeString, }, }, "current_untagged": { Type: schema.TypeList, Computed: true, Elem: &schema.Schema{ Type: schema.TypeString, }, }, KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, "mvrp_forbidden": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "Ports that ignore all MRP messages and remains Not Registered (MT), as well as disables applicant from declaring specific VLAN ID (available since RouterOS 7.15).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "tagged": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "Interface list with a VLAN tag adding action in egress. This setting accepts comma " + "separated values. E.g. tagged=ether1,ether2.", }, "untagged": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "Interface list with a VLAN tag removing action in egress. This setting accepts comma " + "separated values. E.g. untagged=ether3,ether4", }, "vlan_ids": { Type: schema.TypeString, Required: true, Description: "The list of VLAN IDs for certain port configuration. This setting accepts VLAN ID range " + "as well as comma separated values. E.g. vlan-ids=100-115,120,122,128-130.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_detect_internet.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { "detect-interface-list": "none", "internet-interface-list": "none", "lan-interface-list": "none", "wan-interface-list": "none" } */ // https://help.mikrotik.com/docs/spaces/ROS/pages/8323187/Detect+Internet func ResourceInterfaceDetectInternet() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/detect-internet"), MetaId: PropId(Id), "detect_interface_list": { Type: schema.TypeString, Optional: true, Description: "All interfaces in the list will be monitored by Detect Internet.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "internet_interface_list": { Type: schema.TypeString, Optional: true, Description: "Interfaces with state Internet will be dynamically added to this list.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "lan_interface_list": { Type: schema.TypeString, Optional: true, Description: "Interfaces with state Lan will be dynamically added to this list.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "wan_interface_list": { Type: schema.TypeString, Optional: true, Description: "Interfaces with state Wan will be dynamically added to this list.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_detect_internet_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterfaceDetectInternet = "routeros_interface_detect_internet.test" func TestAccInterfaceDetectInternetTest_basic(t *testing.T) { t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccInterfaceDetectInternetConfig("all"), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceDetectInternet), resource.TestCheckResourceAttr(testInterfaceDetectInternet, "internet_interface_list", "all"), resource.TestCheckResourceAttr(testInterfaceDetectInternet, "wan_interface_list", "all"), resource.TestCheckResourceAttr(testInterfaceDetectInternet, "lan_interface_list", "all"), resource.TestCheckResourceAttr(testInterfaceDetectInternet, "detect_interface_list", "all"), ), }, { Config: testAccInterfaceDetectInternetConfig("none"), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceDetectInternet), resource.TestCheckResourceAttr(testInterfaceDetectInternet, "internet_interface_list", "none"), resource.TestCheckResourceAttr(testInterfaceDetectInternet, "wan_interface_list", "none"), resource.TestCheckResourceAttr(testInterfaceDetectInternet, "lan_interface_list", "none"), resource.TestCheckResourceAttr(testInterfaceDetectInternet, "detect_interface_list", "none"), ), }, }, }) }) } } func testAccInterfaceDetectInternetConfig(param string) string { return fmt.Sprintf(`%v resource "routeros_interface_detect_internet" "test" { internet_interface_list = "%v" wan_interface_list = "%v" lan_interface_list = "%v" detect_interface_list = "%v" } `, providerConfig, param, param, param, param) } ================================================ FILE: routeros/resource_interface_dot1x.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) // https://help.mikrotik.com/docs/display/ROS/Dot1X#Dot1X-Client func ResourceInterfaceDot1xClient() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/dot1x/client"), MetaId: PropId(Id), "anon_identity": { Type: schema.TypeString, Optional: true, Description: "Identity for outer layer EAP authentication. Used only with `eap-ttls` and `eap-peap` methods. If not set, the value from the identity parameter will be used for outer layer EAP authentication.", }, "certificate": { Type: schema.TypeString, Optional: true, Default: "none", Description: "Name of a certificate. Required when the `eap-tls` method is used.", }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "eap_methods": { Type: schema.TypeSet, Required: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"eap-tls", "eap-ttls", "eap-peap", "eap-mschapv2"}, false), }, Description: "A set of EAP methods used for authentication: `eap-tls`, `eap-ttls`, `eap-peap`, `eap-mschapv2`.", }, "identity": { Type: schema.TypeString, Required: true, Description: "The supplicant identity that is used for EAP authentication.", }, KeyInterface: PropInterfaceRw, "password": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "Cleartext password for the supplicant.", }, "status": { Type: schema.TypeString, Computed: true, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, SchemaVersion: 1, StateUpgraders: []schema.StateUpgrader{ { Type: ResourceInterfaceDot1xClientV0().CoreConfigSchema().ImpliedType(), Upgrade: stateMigrationScalarToList("eap_methods"), Version: 0, }, }, } } // https://help.mikrotik.com/docs/display/ROS/Dot1X#Dot1X-Server func ResourceInterfaceDot1xServer() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/dot1x/server"), MetaId: PropId(Id), "accounting": { Type: schema.TypeBool, Optional: true, Default: true, Description: "Whether to send RADIUS accounting requests to the authentication server.", }, "auth_timeout": { Type: schema.TypeString, Optional: true, Default: "1m", Description: "Total time available for EAP authentication.", DiffSuppressFunc: TimeEqual, }, "auth_types": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"dot1x", "mac-auth"}, false), }, Description: "Used authentication type on a server interface. Comma-separated list of `dot1x` and `mac-auth`.", }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "guest_vlan_id": { Type: schema.TypeInt, Optional: true, Description: "Assigned VLAN when end devices do not support dot1x authentication and no mac-auth fallback is configured.", ValidateFunc: validation.IntBetween(1, 4094), }, KeyInterface: PropInterfaceRw, "interim_update": { Type: schema.TypeString, Optional: true, Default: "0s", Description: "Interval between scheduled RADIUS Interim-Update messages.", DiffSuppressFunc: TimeEqual, }, "mac_auth_mode": { Type: schema.TypeString, Optional: true, Default: "mac-as-username", Description: "An option that allows to control User-Name and User-Password RADIUS attributes when using MAC authentication.", ValidateFunc: validation.StringInSlice([]string{"mac-as-username", "mac-as-username-and-password"}, false), }, "radius_mac_format": { Type: schema.TypeString, Optional: true, Default: "XX:XX:XX:XX:XX:XX", Description: "An option that controls how the MAC address of the client is encoded in the User-Name and User-Password attributes when using MAC authentication.", ValidateFunc: validation.StringInSlice([]string{"XX-XX-XX-XX-XX-XX", "XX:XX:XX:XX:XX:XX", "XXXXXXXXXXXX", "xx-xx-xx-xx-xx-xx", "xx:xx:xx:xx:xx:xx", "xxxxxxxxxxxx"}, false), }, "reauth_timeout": { Type: schema.TypeString, Optional: true, Description: "An option that enables server port re-authentication.", DiffSuppressFunc: TimeEqual, }, "reject_vlan_id": { Type: schema.TypeInt, Optional: true, Description: "Assigned VLAN when authentication failed, and a RADIUS server responded with an Access-Reject message. ", ValidateFunc: validation.IntBetween(1, 4094), }, "retrans_timeout": { Type: schema.TypeString, Optional: true, Default: "30s", Description: "The time interval between message re-transmissions if no response is received from the supplicant.", DiffSuppressFunc: TimeEqual, }, "server_fail_vlan_id": { Type: schema.TypeInt, Optional: true, Description: "Assigned VLAN when RADIUS server is not responding and request timed out.", ValidateFunc: validation.IntBetween(1, 4094), }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, SchemaVersion: 1, StateUpgraders: []schema.StateUpgrader{ { Type: ResourceInterfaceDot1xServerV0().CoreConfigSchema().ImpliedType(), Upgrade: stateMigrationScalarToList("auth_types"), Version: 0, }, }, } } ================================================ FILE: routeros/resource_interface_dot1x_v0.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) // https://help.mikrotik.com/docs/display/ROS/Dot1X#Dot1X-Client func ResourceInterfaceDot1xClientV0() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/dot1x/client"), MetaId: PropId(Id), "anon_identity": { Type: schema.TypeString, Optional: true, Description: "Identity for outer layer EAP authentication. Used only with `eap-ttls` and `eap-peap` methods. If not set, the value from the identity parameter will be used for outer layer EAP authentication.", }, "certificate": { Type: schema.TypeString, Optional: true, Default: "none", Description: "Name of a certificate. Required when the `eap-tls` method is used.", }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "eap_methods": { Type: schema.TypeString, Required: true, Description: "A list of EAP methods used for authentication: `eap-tls`, `eap-ttls`, `eap-peap`, `eap-mschapv2`.", }, "identity": { Type: schema.TypeString, Required: true, Description: "The supplicant identity that is used for EAP authentication.", }, KeyInterface: PropInterfaceRw, "password": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "Cleartext password for the supplicant.", }, "status": { Type: schema.TypeString, Computed: true, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } // https://help.mikrotik.com/docs/display/ROS/Dot1X#Dot1X-Server func ResourceInterfaceDot1xServerV0() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/dot1x/server"), MetaId: PropId(Id), "accounting": { Type: schema.TypeBool, Optional: true, Default: true, Description: "Whether to send RADIUS accounting requests to the authentication server.", }, "auth_timeout": { Type: schema.TypeString, Optional: true, Default: "1m", Description: "Total time available for EAP authentication.", DiffSuppressFunc: TimeEqual, }, "auth_types": { Type: schema.TypeString, Optional: true, Default: "dot1x", Description: "Used authentication type on a server interface. Comma-separated list of `dot1x` and `mac-auth`.", }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "guest_vlan_id": { Type: schema.TypeInt, Optional: true, Description: "Assigned VLAN when end devices do not support dot1x authentication and no mac-auth fallback is configured.", ValidateFunc: validation.IntBetween(1, 4094), }, KeyInterface: PropInterfaceRw, "interim_update": { Type: schema.TypeString, Optional: true, Default: "0s", Description: "Interval between scheduled RADIUS Interim-Update messages.", DiffSuppressFunc: TimeEqual, }, "mac_auth_mode": { Type: schema.TypeString, Optional: true, Default: "mac-as-username", Description: "An option that allows to control User-Name and User-Password RADIUS attributes when using MAC authentication.", ValidateFunc: validation.StringInSlice([]string{"mac-as-username", "mac-as-username-and-password"}, false), }, "radius_mac_format": { Type: schema.TypeString, Optional: true, Default: "XX:XX:XX:XX:XX:XX", Description: "An option that controls how the MAC address of the client is encoded in the User-Name and User-Password attributes when using MAC authentication.", ValidateFunc: validation.StringInSlice([]string{"XX-XX-XX-XX-XX-XX", "XX:XX:XX:XX:XX:XX", "XXXXXXXXXXXX", "xx-xx-xx-xx-xx-xx", "xx:xx:xx:xx:xx:xx", "xxxxxxxxxxxx"}, false), }, "reauth_timeout": { Type: schema.TypeString, Optional: true, Description: "An option that enables server port re-authentication.", DiffSuppressFunc: TimeEqual, }, "reject_vlan_id": { Type: schema.TypeInt, Optional: true, Description: "Assigned VLAN when authentication failed, and a RADIUS server responded with an Access-Reject message. ", ValidateFunc: validation.IntBetween(1, 4094), }, "retrans_timeout": { Type: schema.TypeString, Optional: true, Default: "30s", Description: "The time interval between message re-transmissions if no response is received from the supplicant.", DiffSuppressFunc: TimeEqual, }, "server_fail_vlan_id": { Type: schema.TypeInt, Optional: true, Description: "Assigned VLAN when RADIUS server is not responding and request timed out.", ValidateFunc: validation.IntBetween(1, 4094), }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_eoip.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) // https://help.mikrotik.com/docs/display/ROS/EoIP func ResourceInterfaceEoip() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/eoip"), MetaId: PropId(Id), KeyActualMtu: PropActualMtuRo, KeyArp: PropArpRw, KeyArpTimeout: PropArpTimeoutRw, KeyAllowFastPath: PropAllowFastPathRw, KeyClampTcpMss: PropClampTcpMssRw, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, KeyDontFragment: PropDontFragmentRw, KeyDscp: PropDscpRw, KeyIpsecSecret: PropIpsecSecretRw, KeyKeepalive: PropKeepaliveRw, KeyL2Mtu: PropL2MtuRo, KeyLocalAddress: PropLocalAddressRw, KeyLoopProtect: PropLoopProtectRw, KeyLoopProtectDisableTime: PropLoopProtectDisableTimeRw, KeyLoopProtectSendInterval: PropLoopProtectSendIntervalRw, KeyLoopProtectStatus: PropLoopProtectStatusRo, KeyMacAddress: PropMacAddressRo, KeyMtu: PropMtuRw(), KeyName: PropNameForceNewRw, KeyRemoteAddress: PropRemoteAddressRw, KeyRunning: PropRunningRo, "tunnel_id": { Type: schema.TypeInt, Optional: true, Default: 0, Description: "Unique tunnel identifier, which must match the other side of the tunnel.", }, } return &schema.Resource{ CreateContext: DefaultValidateCreate(resSchema, func(d *schema.ResourceData) diag.Diagnostics { if d.Get("allow_fast_path").(bool) && d.Get("ipsec_secret").(string) != "" { return diag.Errorf("can't enable fastpath together with ipsec") } return nil }), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultValidateUpdate(resSchema, func(d *schema.ResourceData) diag.Diagnostics { if d.Get("allow_fast_path").(bool) && d.Get("ipsec_secret").(string) != "" { return diag.Errorf("can't enable fastpath together with ipsec") } return nil }), DeleteContext: DefaultDelete(resSchema), SchemaVersion: 1, StateUpgraders: []schema.StateUpgrader{ { Type: ResourceInterfaceEoipV0().CoreConfigSchema().ImpliedType(), Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), Version: 0, }, }, Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_eoip_v0.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func ResourceInterfaceEoipV0() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/eoip"), MetaId: PropId(Name), KeyActualMtu: PropActualMtuRo, KeyArp: PropArpRw, KeyArpTimeout: PropArpTimeoutRw, KeyAllowFastPath: PropAllowFastPathRw, KeyClampTcpMss: PropClampTcpMssRw, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, KeyDontFragment: PropDontFragmentRw, KeyDscp: PropDscpRw, KeyIpsecSecret: PropIpsecSecretRw, KeyKeepalive: PropKeepaliveRw, KeyL2Mtu: PropL2MtuRo, KeyLocalAddress: PropLocalAddressRw, KeyLoopProtect: PropLoopProtectRw, KeyLoopProtectDisableTime: PropLoopProtectDisableTimeRw, KeyLoopProtectSendInterval: PropLoopProtectSendIntervalRw, KeyLoopProtectStatus: PropLoopProtectStatusRo, KeyMacAddress: PropMacAddressRo, KeyMtu: PropMtuRw(), KeyName: PropNameForceNewRw, KeyRemoteAddress: PropRemoteAddressRw, KeyRunning: PropRunningRo, "tunnel_id": { Type: schema.TypeInt, Optional: true, Default: 0, Description: "Unique tunnel identifier, which must match the other side of the tunnel.", }, }, } } ================================================ FILE: routeros/resource_interface_ethernet.go ================================================ package routeros import ( "context" "errors" "fmt" "regexp" "strings" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "advertise": "10M-half,10M-full,100M-half,100M-full,1000M-full", "arp": "enabled", "arp-timeout": "auto", "auto-negotiation": "true", "cable-settings": "default", "default-name": "ether1", "disable-running-check": "true", "disabled": "false", "loop-protect": "default", "loop-protect-disable-time": "5m", "loop-protect-send-interval": "5s", "loop-protect-status": "off", "mac-address": "54:05:AB:1E:BE:71", "mtu": "1500", "name": "ether1", "orig-mac-address": "54:05:AB:1E:BE:71", "running": "true", "rx-broadcast": "250", "rx-bytes": "222253", "rx-flow-control": "off", "rx-multicast": "10", "rx-packet": "1889", "tx-broadcast": "113", "tx-bytes": "693931", "tx-flow-control": "off", "tx-multicast": "270", "tx-packet": "2222" } */ // ResourceInterfaceEthernet is the schema for ethernet interfaces // https://help.mikrotik.com/docs/display/ROS/Ethernet#Ethernet-Properties func ResourceInterfaceEthernet() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/ethernet"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("factory_name", "driver_rx_byte", "driver_rx_packet", "driver_tx_byte", "driver_tx_packet", "rx_64", "rx_65_127", "rx_128_255", "rx_256_511", "rx_512_1023", "rx_1024_1518", "rx_1519_max", "tx_64", "tx_65_127", "tx_128_255", "tx_256_511", "tx_512_1023", "tx_1024_1518", "tx_1519_max", "tx_rx_64", "tx_rx_65_127", "tx_rx_128_255", "tx_rx_256_511", "tx_rx_512_1023", "tx_rx_1024_1518", "tx_rx_1024_max", "tx_rx_1519_max", "rx_broadcast", "rx_bytes", "rx_control", "rx_drop", "rx_fcs_error", "rx_fragment", "rx_jabber", "rx_multicast", "rx_packet", "rx_pause", "rx_too_short", "rx_too_long", "tx_broadcast", "tx_bytes", "tx_control", "tx_drop", "tx_drop_byte", "tx_drop_packet", "tx_drop_queue0_byte", "tx_drop_queue0_packet", "tx_drop_queue1_byte", "tx_drop_queue1_packet", "tx_drop_queue2_byte", "tx_drop_queue2_packet", "tx_drop_queue3_byte", "tx_drop_queue3_packet", "tx_drop_queue4_byte", "tx_drop_queue4_packet", "tx_drop_queue5_byte", "tx_drop_queue5_packet", "tx_drop_queue6_byte", "tx_drop_queue6_packet", "tx_drop_queue7_byte", "tx_drop_queue7_packet", "tx_fcs_error", "tx_fragment", "tx_jabber", "tx_multicast", "tx_packet", "tx_pause", "tx_too_short", "tx_too_long", "rx_align_error", "rx_carrier_error", "rx_code_error", "rx_error_events", "rx_length_error", "rx_overflow", "rx_unicast", "rx_unknown_op", "tx_collision", "tx_excessive_collision", "tx_late_collision", "tx_multiple_collision", "tx_single_collision", "tx_total_collision", "tx_deferred", "tx_excessive_deferred", "tx_unicast", "tx_underrun", "rx_tcp_checksum_error", "rx_udp_checksum_error", "rx_ip_header_checksum_error", "tx_carrier_sense_error", ), "advertise": { Type: schema.TypeString, Optional: true, Description: ` Advertised speed and duplex modes for Ethernet interfaces over twisted pair, only applies when auto-negotiation is enabled. Advertising higher speeds than the actual interface supported speed will have no effect, multiple options are allowed.`, // ValidateFunc: validation.StringMatch( // regexp.MustCompile(`^[0-9\.]+(M|G)-(full|half|base\w+(-\w+)?)$`), // "Since RouterOS v7.12 the values of this property have changed. Please check the documentation.", // ), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyArp: PropArpRw, KeyArpTimeout: PropArpTimeoutRw, "auto_negotiation": { Type: schema.TypeBool, Optional: true, Description: `When enabled, the interface "advertises" its maximum capabilities to achieve the best connection possible. Note1: Auto-negotiation should not be disabled on one end only, otherwise Ethernet Interfaces may not work properly. Note2: Gigabit Ethernet and NBASE-T Ethernet links cannot work with auto-negotiation disabled.`, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "bandwidth": { Type: schema.TypeString, Optional: true, Description: `Sets max rx/tx bandwidth in kbps that will be handled by an interface. TX limit is supported on all Atheros switch-chip ports. RX limit is supported only on Atheros8327/QCA8337 switch-chip ports.`, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "cable_settings": { Type: schema.TypeString, Optional: true, Description: `Changes the cable length setting (only applicable to NS DP83815/6 cards)`, ValidateFunc: validation.StringInSlice([]string{"default", "short", "standard"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "combo_mode": { Type: schema.TypeString, Optional: true, Description: `When auto mode is selected, the port that was first connected will establish the link. In case this link fails, the other port will try to establish a new link. If both ports are connected at the same time (e.g. after reboot), the priority will be the SFP/SFP+ port. When sfp mode is selected, the interface will only work through SFP/SFP+ cage. When copper mode is selected, the interface will only work through RJ45 Ethernet port.`, ValidateFunc: validation.StringInSlice([]string{"auto", "copper", "sfp"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyComment: PropCommentRw, KeyDefaultName: PropDefaultNameRo("The default name for an interface."), KeyDisabled: PropDisabledRw, "disable_running_check": { Type: schema.TypeBool, Optional: true, Description: `Disable running check. If this value is set to 'no', the router automatically detects whether the NIC is connected with a device in the network or not. Default value is 'yes' because older NICs do not support it. (only applicable to x86)`, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "factory_name": { Type: schema.TypeString, Required: true, Description: "The factory name of the identifier, serves as resource identifier. Determines which interface will be updated.", }, "fec_mode": { Type: schema.TypeString, Optional: true, Description: "Changes Forward Error Correction (FEC) mode for SFP28, QSFP+ and QSFP28 interfaces. " + "Same mode should be used on both link ends, otherwise FEC mismatch could result in non-working link " + "or even false link-ups. ", ValidateFunc: validation.StringInSlice([]string{"auto", "fec74", "fec91", "off"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "full_duplex": { Type: schema.TypeBool, Optional: true, Description: `Defines whether the transmission of data appears in two directions simultaneously, only applies when auto-negotiation is disabled.`, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyL2Mtu: PropL2MtuRw, KeyLoopProtect: PropLoopProtectRw, KeyLoopProtectDisableTime: PropLoopProtectDisableTimeRw, KeyLoopProtectSendInterval: PropLoopProtectSendIntervalRw, KeyLoopProtectStatus: PropLoopProtectStatusRo, "mac_address": { Type: schema.TypeString, Optional: true, Description: `Media Access Control number of an interface.`, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "mdix_enable": { Type: schema.TypeBool, Optional: true, Description: `Whether the MDI/X auto cross over cable correction feature is enabled for the port (Hardware specific, e.g. ether1 on RB500 can be set to yes/no. Fixed to 'yes' on other hardware.)`, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "mtu": { Type: schema.TypeInt, Optional: true, Description: "Layer3 Maximum transmission unit", ValidateFunc: validation.IntBetween(0, 65536), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyName: PropName("Name of the ethernet interface."), "orig_mac_address": { Type: schema.TypeString, Computed: true, Description: "Original Media Access Control number of an interface. (read only)", }, "poe_lldp_enabled": { Type: schema.TypeBool, Optional: true, Description: "An option that enables LLDP for managing devices.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "poe_out": { Type: schema.TypeString, Optional: true, Description: "PoE settings: (https://wiki.mikrotik.com/wiki/Manual:PoE-Out)", ValidateFunc: validation.StringInSlice([]string{"auto-on", "forced-on", "off"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "poe_priority": { Type: schema.TypeInt, Optional: true, Description: "PoE settings: (https://wiki.mikrotik.com/wiki/Manual:PoE-Out)", ValidateFunc: validation.IntBetween(0, 99), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "poe_voltage": { Type: schema.TypeString, Optional: true, Description: "An option that allows us to manually control the voltage outputs on the PoE port.", ValidateFunc: validation.StringInSlice([]string{"auto", "high", "low"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "power_cycle_interval": { Type: schema.TypeString, Optional: true, Description: "An options that disables PoE-Out power for 5s between the specified intervals.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "power_cycle_ping_enabled": { Type: schema.TypeBool, Optional: true, Description: "An option that enables ping watchdog of power cycles on the port if a host does not respond to ICMP or MAC-Telnet packets.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "power_cycle_ping_address": { Type: schema.TypeString, Optional: true, Description: "An address to monitor.", ValidateFunc: validation.IsIPAddress, RequiredWith: []string{"power_cycle_ping_enabled"}, }, "power_cycle_ping_timeout": { Type: schema.TypeString, Optional: true, Description: "If the host does not respond over the specified period, the PoE-Out port is switched off for 5s.", DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { return AlwaysPresentNotUserProvided(k, old, new, d) || TimeEqual(k, old, new, d) }, }, "running": { Type: schema.TypeBool, Computed: true, Description: "Whether interface is running. Note that some interface does not have running check and they are always reported as \"running\"", }, "rx_flow_control": { Type: schema.TypeString, Optional: true, Description: `When set to on, the port will process received pause frames and suspend transmission if required. auto is the same as on except when auto-negotiation=yes flow control status is resolved by taking into account what other end advertises.`, ValidateFunc: validation.StringInSlice([]string{"on", "off", "auto"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "sfp_rate_select": { Type: schema.TypeString, Optional: true, Description: `Allows to control rate select pin for SFP ports. Values: high | low`, ValidateFunc: validation.StringInSlice([]string{"high", "low"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "sfp_shutdown_temperature": { Type: schema.TypeInt, Optional: true, Description: "The temperature in Celsius at which the interface will be temporarily turned off due to too high detected SFP module temperature (introduced v6.48)." + "The default value for SFP/SFP+/SFP28 interfaces is 95, and for QSFP+/QSFP28 interfaces 80 (introduced v7.6).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "sfp_ignore_rx_los": { Type: schema.TypeBool, Optional: true, Description: "An option to ignore RX LOS (Loss of Signal) status of the SFP module.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "slave": { Type: schema.TypeBool, Computed: true, Description: "Whether interface is configured as a slave of another interface (for example Bonding)", }, "speed": { Type: schema.TypeString, Optional: true, Description: "Sets interface data transmission speed which takes effect only when ```auto_negotiation``` is disabled.", ValidateFunc: validation.StringMatch( regexp.MustCompile(`^[0-9\.]+(M|G)(?:(bps)|(-base\w+)(-\w+)?)$`), "Since RouterOS v7.12 the values of this property have changed. Please check the documentation.", ), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "switch": { Type: schema.TypeString, Computed: true, Description: "ID to which switch chip interface belongs to.", }, "tx_flow_control": { Type: schema.TypeString, Optional: true, Description: `When set to on, the port will generate pause frames to the upstream device to temporarily stop the packet transmission. Pause frames are only generated when some routers output interface is congested and packets cannot be transmitted anymore. Auto is the same as on except when auto-negotiation=yes flow control status is resolved by taking into account what other end advertises.`, ValidateFunc: validation.StringInSlice([]string{"on", "off", "auto"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: updateOnlyDeviceCreate(resSchema), ReadContext: updateOnlyDeviceRead(resSchema), UpdateContext: updateOnlyDeviceUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } func updateOnlyDeviceCreate(s map[string]*schema.Schema) schema.CreateContextFunc { return func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { return updateEthernetInterface(ctx, s, d, m) } } func updateOnlyDeviceUpdate(s map[string]*schema.Schema) schema.UpdateContextFunc { return func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { return updateEthernetInterface(ctx, s, d, m) } } func updateOnlyDeviceRead(s map[string]*schema.Schema) schema.ReadContextFunc { return func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { return readEthernetInterface(ctx, s, d, m) } } func readEthernetInterface(ctx context.Context, s map[string]*schema.Schema, d *schema.ResourceData, m interface{}) diag.Diagnostics { var ethernetInterface MikrotikItem var err error // The item has already an ID, we don't need to perform the lookup if val := d.Id(); val != "" { tflog.Debug(ctx, "fetching ethernet interface by id", map[string]interface{}{"id": val}) metadata := GetMetadata(s) items, err := ReadItems(&ItemId{metadata.IdType, val}, metadata.Path, m.(Client)) if err != nil { return diag.FromErr(fmt.Errorf("reading interface by id: %w", err)) } if len(*items) > 1 { return diag.FromErr(fmt.Errorf("more than 1 interface returned when fetching by id %v", val)) } if len(*items) == 0 { return diag.FromErr(fmt.Errorf("unable to find interface when fetching by id: %v", val)) } ethernetInterface = (*items)[0] } else { // As We don't know the ID, we have to look it up by "default"/"factory" name ethernetInterface, err = findInterfaceByDefaultName(s, d, m.(Client)) if err != nil { return diag.FromErr(err) } } s = updateSchemaWithRouterCapabilities(s, ethernetInterface) return DefaultRead(s)(ctx, d, m) } // updateEthernetInterface searches for the interface and disables fields not supported by the router instance func updateEthernetInterface(ctx context.Context, s map[string]*schema.Schema, d *schema.ResourceData, m interface{}) diag.Diagnostics { if val := d.Id(); val == "" { ethernetInterface, err := findInterfaceByDefaultName(s, d, m.(Client)) if err != nil { return diag.FromErr(err) } d.SetId(ethernetInterface.GetID(Id)) } if updateDiag := ResourceUpdate(ctx, s, d, m); updateDiag.HasError() { return updateDiag } return readEthernetInterface(ctx, s, d, m) } func updateSchemaWithRouterCapabilities(s map[string]*schema.Schema, item MikrotikItem) map[string]*schema.Schema { // Dynamic schema, counters for tx_queue${number}_packets, changes from router to router, read only counters. // Just drop them as they don't have much sense in the context of a terraform provider for key := range item { if strings.HasPrefix(key, "tx-queue") { s[MetaSkipFields].Default = skipFieldInSchema(s[MetaSkipFields].Default, KebabToSnake(key)) } } return s } func findInterfaceByDefaultName(s map[string]*schema.Schema, d *schema.ResourceData, c Client) (MikrotikItem, error) { metadata := GetMetadata(s) filter := buildReadFilter(map[string]interface{}{"default-name": d.Get("factory_name")}) items, err := ReadItemsFiltered(filter, metadata.Path, c) if err != nil { return nil, err } if items == nil || len(*items) == 0 { return nil, errors.New("unable to find interface") } ethernetInterface := (*items)[0] return ethernetInterface, nil } func skipFieldInSchema(defaults interface{}, field string) string { return fmt.Sprintf("%s,\"%s\"", defaults, field) } ================================================ FILE: routeros/resource_interface_ethernet_switch.go ================================================ package routeros import ( "context" "fmt" "regexp" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id":"*0", "cpu-flow-control":"true", "driver-rx-byte":"47500186966", "driver-rx-packet":"107352097", "driver-tx-byte":"202118920815", "driver-tx-packet":"175081916", "invalid":"false", "mirror-source":"none", "mirror-target":"none", "name":"switch1", "rx-align-error":"0", "rx-broadcast":"1049998", "rx-bytes":"55163404001", "rx-carrier-error":"0", "rx-code-error":"0", "rx-control":"0", "rx-drop":"0", "rx-fcs-error":"0", "rx-fragment":"0", "rx-jabber":"0", "rx-length-error":"0", "rx-multicast":"5101395", "rx-packet":"145932800", "rx-pause":"0", "rx-too-long":"75", "rx-too-short":"0", "rx-unknown-op":"0", "tx-broadcast":"276405", "tx-bytes":"202693687261", "tx-control":"0", "tx-deferred":"0", "tx-drop":"0", "tx-excessive-collision":"0", "tx-excessive-deferred":"0", "tx-fcs-error":"0", "tx-fragment":"0", "tx-jabber":"0", "tx-late-collision":"0", "tx-multicast":"737076", "tx-multiple-collision":"0", "tx-packet":"175076294", "tx-pause":"0", "tx-rx-1024-1518":"169188042", "tx-rx-128-255":"45589148", "tx-rx-1519-max":"0", "tx-rx-256-511":"4444935", "tx-rx-512-1023":"3890955", "tx-rx-64":"5245202", "tx-rx-65-127":"92433137", "tx-single-collision":"0", "tx-too-long":"218648", "tx-too-short":"0", "tx-total-collision":"0", "type":"QCA-8337" } [=Oo=@mikrotik]/interface/ethernet/switch/print detail Flags: I - invalid 0 name="switch1" type=QCA-8337 mirror-source=none mirror-target=none mirror-egress-target=none cpu-flow-control=yes l3-hw-offloading=no */ // https://help.mikrotik.com/docs/display/ROS/Switch+Chip+Features#SwitchChipFeatures-CPUFlowControl // https://help.mikrotik.com/docs/display/ROS/L3+Hardware+Offloading func ResourceInterfaceEthernetSwitch() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/ethernet/switch"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("switch_id", "driver_rx_byte", "driver_rx_packet", "driver_tx_byte", "driver_tx_packet", "rx_align_error", "rx_broadcast", "rx_bytes", "rx_carrier_error", "rx_code_error", "rx_control", "rx_drop", "rx_fcs_error", "rx_fragment", "rx_jabber", "rx_length_error", "rx_multicast", "rx_packet", "rx_pause", "rx_too_long", "rx_too_short", "rx_unknown_op", "tx_broadcast", "tx_bytes", "tx_control", "tx_deferred", "tx_drop", "tx_excessive_collision", "tx_excessive_deferred", "tx_fcs_error", "tx_fragment", "tx_jabber", "tx_late_collision", "tx_multicast", "tx_multiple_collision", "tx_packet", "tx_pause", "tx_rx_1024_1518", "tx_rx_128_255", "tx_rx_1519_max", "tx_rx_256_511", "tx_rx_512_1023", "tx_rx_64", "tx_rx_65_127", "tx_single_collision", "tx_too_long", "tx_too_short", "tx_total_collision", "not_learned", "custom_drop_packet"), "cpu_flow_control": { Type: schema.TypeBool, Optional: true, Description: "All switch chips have a special port that is called switchX-cpu, this is the CPU port for a " + "switch chip, it is meant to forward traffic from a switch chip to the CPU, such a port is required " + "for management traffic and for routing features. By default the switch chip ensures that this " + "special CPU port is not congested and sends out Pause Frames when link capacity is exceeded to make " + "sure the port is not oversaturated, this feature is called CPU Flow Control. Without this feature " + "packets that might be crucial for routing or management purposes might get dropped.", }, KeyInvalid: PropInvalidRo, "l3_hw_offloading": { Type: schema.TypeBool, Optional: true, Description: "Layer 3 Hardware Offloading (L3HW, otherwise known as IP switching or HW routing) allows to " + "offload some router features onto the switch chip. This allows reaching wire speeds when routing " + "packets, which simply would not be possible with the CPU.", }, // "mirror_egress_target": { // Type: schema.TypeString, // Optional: true, // Default: "none", // Description: "Selects a single mirroring egress target port, only available on 88E6393X, 88E6191X and " + // "88E6190 switch chips. Mirrored packets from mirror-egress (see the property in port menu) will be " + // "sent to the selected port.", // }, "mirror_source": { Type: schema.TypeString, Optional: true, Description: "Selects a single mirroring source port. Ingress and egress traffic will be sent to the " + "mirror-target port. Note that mirror-target port has to belong to the same switch (see which port " + "belongs to which switch in /interface ethernet menu).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "mirror_target": { Type: schema.TypeString, Optional: true, Description: "Selects a single mirroring target port. Mirrored packets from mirror-source and mirror " + "(see the property in rule and host table) will be sent to the selected port.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "mirror_egress_target": { Type: schema.TypeString, Optional: true, Description: "Selects a single mirroring egress target port, only available on 88E6393X, 88E6191X and " + "88E6190 switch chips. Mirrored packets from `mirror-egress` (see the property in port menu) will be sent " + "to the selected port.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyName: PropName("Name of the switch."), "qos_hw_offloading": { Type: schema.TypeBool, Optional: true, Description: "Allows enabling QoS for the given switch chip (if the latter supports QoS).", }, "rspan": { Type: schema.TypeBool, Optional: true, Description: "Enables Remote Switch Port Analyzer (RSPAN) feature on mirror-target. Traffic marked for " + "ingress or egress mirroring is carried over a specified remote analyzer VLAN - `rspan-egress-vlan-id` " + "and `rspan-ingress-vlan-id`.", }, "rspan_ingress_vlan_id": { Type: schema.TypeInt, Optional: true, Description: "RSPAN ingress VLAN Id.", ValidateFunc: validation.IntBetween(1, 4094), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "rspan_egress_vlan_id": { Type: schema.TypeInt, Optional: true, Description: "RSPAN egress VLAN Id.", ValidateFunc: validation.IntBetween(1, 4094), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "switch_id": { Type: schema.TypeString, Optional: true, Default: "*0", Description: "Switch-chip id. Default .id = *0", ValidateFunc: validation.StringMatch(regexp.MustCompile(`^\*\d+$`), "The string must contain an identifier in MikroTik format: '*0'"), }, "type": { Type: schema.TypeString, Computed: true, Description: "Switch-chip type.", }, } resRead := func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { metadata := GetMetadata(resSchema) res, err := ReadItems(&ItemId{metadata.IdType, d.Id()}, metadata.Path, m.(Client)) if err != nil { ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgGet, err)) return diag.FromErr(err) } // Resource not found. if len(*res) == 0 { d.SetId("") return nil } id := (*res)[0].GetID(metadata.IdType) d.SetId(id) d.Set("switch_id", id) return MikrotikResourceDataToTerraform((*res)[0], resSchema, d) } resCreateUpdate := func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { if d.Id() == "" { d.SetId(d.Get("switch_id").(string)) } item, metadata := TerraformResourceDataToMikrotik(resSchema, d) var resUrl string if m.(Client).GetTransport() == TransportREST { resUrl = "/set" } item[".id"] = d.Id() err := m.(Client).SendRequest(crudPost, &URL{Path: metadata.Path + resUrl}, item, nil) if err != nil { return diag.FromErr(err) } return resRead(ctx, d, m) } return &schema.Resource{ CreateContext: resCreateUpdate, ReadContext: resRead, UpdateContext: resCreateUpdate, DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_ethernet_switch_crs.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { "bridge-type": "customer-vid-used-as-lookup-vid", "bypass-ingress-port-policing-for": "", "bypass-l2-security-check-filter-for": "", "bypass-vlan-ingress-filter-for": "", "drop-if-invalid-or-src-port-not-member-of-vlan-on-ports": "ether1,ether2,ether3,ether4,ether5,ether6,ether7,ether8", "drop-if-no-vlan-assignment-on-ports": "", "egress-mirror-ratio": "1/1", "egress-mirror0": "switch1-cpu,modified", "egress-mirror1": "switch1-cpu,modified", "fdb-uses": "mirror0", "forward-unknown-vlan": "true", "ingress-mirror-ratio": "1/1", "ingress-mirror0": "switch1-cpu,unmodified", "ingress-mirror1": "switch1-cpu,unmodified", "mac-level-isolation": "true", "mirror-egress-if-ingress-mirrored": "false", "mirror-tx-on-mirror-port": "false", "mirrored-packet-drop-precedence": "green", "mirrored-packet-qos-priority": "0", "multicast-lookup-mode": "dst-ip-and-vid-for-ipv4", "name": "switch1", "override-existing-when-ufdb-full": "false", "type": "QCA-8511", "unicast-fdb-timeout": "5m", "unknown-vlan-lookup-mode": "svl", "use-cvid-in-one2one-vlan-lookup": "true", "use-svid-in-one2one-vlan-lookup": "false", "vlan-uses": "mirror0" } */ // https://help.mikrotik.com/docs/spaces/ROS/pages/103841835/CRS1xx+and+2xx+series+switches#CRS1xxand2xxseriesswitches-GlobalSettings func ResourceInterfaceEthernetSwitchCrs() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/ethernet/switch"), MetaId: PropId(Id), "bridge_type": { Type: schema.TypeString, Optional: true, Description: "The bridge type defines which VLAN tag is used as Lookup-VID. Lookup-VID serves as the VLAN " + "key for all VLAN-based lookups.", ValidateFunc: validation.StringInSlice([]string{"customer-vid-used-as-lookup-vid", "service-vid-used-as-lookup-vid"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "bypass_ingress_port_policing_for": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "Protocols that are excluded from Ingress Port Policing. (arp, dhcpv4, dhcpv6, eapol, igmp, " + "mld, nd, pppoe-discovery, ripv1).", }, "bypass_l2_security_check_filter_for": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "Protocols that are excluded from Policy rule security check. (arp, dhcpv4, dhcpv6, eapol, " + "igmp, mld, nd, pppoe-discovery, ripv1).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "bypass_vlan_ingress_filter_for": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "Protocols that are excluded from Ingress VLAN filtering. These protocols are not dropped if " + "they have invalid VLAN. (arp, dhcpv4, dhcpv6,eapol, igmp, mld, nd, pppoe-discovery, ripv1).", }, "drop_if_invalid_or_src_port_not_member_of_vlan_on_ports": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "Ports that drop invalid and other port VLAN ID frames.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "drop_if_no_vlan_assignment_on_ports": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "Ports which drop frames if no MAC-based, Protocol-based VLAN assignment or Ingress VLAN Translation " + "is applied.", }, "egress_mirror_ratio": { Type: schema.TypeString, Optional: true, Description: "Proportion of egress mirrored packets compared to all packets.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "egress_mirror0": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "The first egress mirroring analyzer port or trunk and mirroring format:analyzer-configured " + "- The packet is the same as the packet to the destination. VLAN format is modified based on the VLAN " + "configurations of the analyzer port.modified - The packet is the same as the packet to the destination. " + "VLAN format is modified based on the VLAN configurations of the egress port.original - Traffic is mirrored " + "without any change to the original incoming packet format. But the service VLAN tag is stripped in the " + "edge port.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "egress_mirror1": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "The second egress mirroring analyzer port or trunk and mirroring format:analyzer-configured " + "- The packet is the same as the packet to the destination. VLAN format is modified based on the VLAN " + "configurations of the analyzer port.modified - The packet is the same as the packet to the destination. " + "VLAN format is modified based on the VLAN configurations of the egress port.original - Traffic is mirrored " + "without any change to the original incoming packet format. But the service VLAN tag is stripped in the " + "edge port.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "fdb_uses": { Type: schema.TypeString, Optional: true, Description: "Analyzer port used for FDB-based mirroring.", ValidateFunc: validation.StringInSlice([]string{"mirror0", "mirror1"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "forward_unknown_vlan": { Type: schema.TypeBool, Optional: true, Description: "Whether to allow forwarding VLANs that are not members of the VLAN table.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "ingress_mirror_ratio": { Type: schema.TypeString, Optional: true, Description: "The proportion of ingress mirrored packets compared to all packets.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "ingress_mirror0": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "The first ingress mirroring analyzer port or trunk and mirroring format:analyzer-configured " + "- The packet is the same as the packet to the destination. VLAN format is modified based on the VLAN " + "configurations of the analyzer port.modified - The packet is the same as the packet to the destination. " + "VLAN format is modified based on the VLAN configurations of the egress port.original - Traffic is mirrored " + "without any change to the original incoming packet format. But the service VLAN tag is stripped in the " + "edge port.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "ingress_mirror1": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "The second ingress mirroring analyzer port or trunk and mirroring format:analyzer-configured " + "- The packet is the same as the packet to the destination. VLAN format is modified based on the VLAN " + "configurations of the analyzer port.modified - The packet is the same as the packet to the destination. " + "VLAN format is modified based on the VLAN configurations of the egress port.original - Traffic is mirrored " + "without any change to the original incoming packet format. But the service VLAN tag is stripped in the " + "edge port.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "mac_level_isolation": { Type: schema.TypeBool, Optional: true, Description: "Globally enables or disables MAC level isolation. Once enabled, the switch will check the " + "source and destination MAC address entries and their isolation-profile from the unicast forwarding table. " + "By default, the switch will learn MAC addresses and place them into a promiscuous isolation profile. " + "Other isolation profiles can be used when creating static unicast entries. If the source or destination " + "MAC address is located on a promiscuous isolation profile, the packet is forwarded. If both source and " + "destination MAC addresses are located on the same community1 or community2 isolation profile, the packet " + "is forwarded. The packet is dropped when the source and destination MAC address isolation profile is " + "isolated, or when the source and destination MAC address isolation profiles are from different communities " + "(e.g. source MAC address is community1 and destination MAC address is community2). When MAC level isolation " + "is globally disabled, the isolation is bypassed.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "mirror_egress_if_ingress_mirrored": { Type: schema.TypeBool, Optional: true, Description: "When a packet is applied to both ingress and egress mirroring, only ingress mirroring is performed " + "on the packet, if this setting is disabled. If this setting is enabled both mirroring types are applied.", }, "mirror_tx_on_mirror_port": { Type: schema.TypeBool, Optional: true, Description: "", }, "mirrored_packet_drop_precedence": { Type: schema.TypeString, Optional: true, Description: "Remarked drop precedence in mirrored packets. This QoS attribute is used for mirrored packet " + "enqueuing or dropping.", ValidateFunc: validation.StringInSlice([]string{"drop", "green", "red", "yellow"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "mirrored_packet_qos_priority": { Type: schema.TypeInt, Optional: true, Description: "Remarked priority in mirrored packets.", ValidateFunc: validation.IntBetween(0, 7), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "multicast_lookup_mode": { Type: schema.TypeString, Optional: true, Description: "Lookup mode for IPv4 multicast bridging.dst-mac-and-vid-always - For all packet types lookup " + "key is the destination MAC and VLAN ID.dst-ip-and-vid-for-ipv4 - For IPv4 packets lookup key is the " + "destination IP and VLAN ID. For other packet types, the lookup key is the destination MAC and VLAN ID.", ValidateFunc: validation.StringInSlice([]string{"dst-ip-and-vid-for-ipv4", "dst-mac-and-vid-always"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyName: PropName("Name of the switch."), "type": { Type: schema.TypeString, Computed: true, Description: "Switch-chip type.", }, "override_existing_when_ufdb_full": { Type: schema.TypeBool, Optional: true, Description: "Enable or disable to override existing entry which has the lowest aging value when UFDB is " + "full.", }, "unicast_fdb_timeout": { Type: schema.TypeString, Optional: true, Description: "Timeout for Unicast FDB entries.", DiffSuppressFunc: TimeEqual, }, "unknown_vlan_lookup_mode": { Type: schema.TypeString, Optional: true, Description: "Lookup and learning mode for packets with invalid VLAN.", ValidateFunc: validation.StringInSlice([]string{"ivl", "svl"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "use_cvid_in_one2one_vlan_lookup": { Type: schema.TypeBool, Optional: true, Description: "Whether to use customer VLAN ID for 1:1 VLAN switching lookup.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "use_svid_in_one2one_vlan_lookup": { Type: schema.TypeBool, Optional: true, Description: "Whether to use service VLAN ID for 1:1 VLAN switching lookup.", }, "vlan_uses": { Type: schema.TypeString, Optional: true, Description: "Analyzer port used for VLAN-based mirroring.", ValidateFunc: validation.StringInSlice([]string{"mirror0", "mirror1"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ Description: "Resource for managing CRS (Cloud Router Switch) series device properties.", CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_ethernet_switch_crs_egress_vlan_tag.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*2", "comment": "", "disabled": "false", "dynamic": "false", "tagged-ports": "ether1,ether2,ether3,ether4,ether5,ether6", "vlan-id": "100" } */ // https://help.mikrotik.com/docs/spaces/ROS/pages/103841835/CRS1xx+and+2xx+series+switches#CRS1xxand2xxseriesswitches-EgressVLANTag func ResourceInterfaceEthernetSwitchCrsEgressVlanTag() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/ethernet/switch/egress-vlan-tag"), MetaId: PropId(Id), KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, "tagged_ports": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "Ports that are tagged in egress.", }, KeyVlanId: PropVlanIdRw("VLAN ID which is tagged in egress.", false), } return &schema.Resource{ Description: "Resource for managing CRS (Cloud Router Switch) series device properties.", CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_ethernet_switch_crs_egress_vlan_translation.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", ".nextid": "*FFFFFFFF", "comment": "", "customer-vid": "200", "customer-vlan-format": "any", "disabled": "false", "dynamic": "false", "new-customer-vid": "0", "pcp-propagation": "false", "ports": "ether1", "service-vlan-format": "any" } */ // https://help.mikrotik.com/docs/spaces/ROS/pages/103841835/CRS1xx+and+2xx+series+switches#CRS1xxand2xxseriesswitches-Ingress%2FEgressVLANTranslation func ResourceInterfaceEthernetSwitchCrsEgressVlanTranslation() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/ethernet/switch/egress-vlan-translation"), MetaId: PropId(Id), KeyComment: PropCommentRw, "customer_dei": { Type: schema.TypeString, Optional: true, Description: "Matching DEI of the customer tag.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "customer_pcp": { Type: schema.TypeString, Optional: true, Description: "Matching PCP of the customer tag.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "customer_vid": { Type: schema.TypeString, Optional: true, Description: "Matching the VLAN ID of the customer tag.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "customer_vlan_format": { Type: schema.TypeString, Optional: true, Description: "Type of frames with customer tag for which VLAN translation rule is valid.", ValidateFunc: validation.StringInSlice([]string{"any", "priority-tagged-or-tagged", "tagged", "untagged-or-tagged"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, "new_customer_vid": { Type: schema.TypeString, Optional: true, Description: "The new customer VLAN ID replaces the matching customer VLAN ID. If set to 4095 and ingress " + "VLAN translation is used, then traffic is dropped.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "new_service_vid": { Type: schema.TypeString, Optional: true, Description: "The new service VLAN ID replaces the matching service VLAN ID.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "pcp_propagation": { Type: schema.TypeString, Optional: true, Description: "Enables or disables PCP propagation.If the port type is Edge, the customer PCP is copied from " + "the service PCP.If the port type is Network, the service PCP is copied from the customer PCP.", }, "ports": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "Matching switch ports for VLAN translation rule.", }, "service_dei": { Type: schema.TypeString, Optional: true, Description: "Matching DEI of the service tag.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "service_pcp": { Type: schema.TypeString, Optional: true, Description: "Matching PCP of the service tag.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "service_vid": { Type: schema.TypeString, Optional: true, Description: "Matching VLAN ID of the service tag.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "service_vlan_format": { Type: schema.TypeString, Optional: true, Description: "Type of frames with service tag for which VLAN translation rule is valid.", ValidateFunc: validation.StringInSlice([]string{"any", "priority-tagged-or-tagged", "tagged", "untagged-or-tagged"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "swap_vids": { Type: schema.TypeString, Optional: true, Description: "", }, } return &schema.Resource{ Description: "Resource for managing CRS (Cloud Router Switch) series device properties.", CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_ethernet_switch_crs_ingress_vlan_translation.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*2", ".nextid": "*3", "comment": "", "customer-vid": "0", "customer-vlan-format": "any", "disabled": "false", "dynamic": "false", "new-customer-vid": "200", "pcp-propagation": "false", "ports": "ether7", "sa-learning": "true", "service-vlan-format": "any" } */ // https://help.mikrotik.com/docs/spaces/ROS/pages/103841835/CRS1xx+and+2xx+series+switches#CRS1xxand2xxseriesswitches-Ingress%2FEgressVLANTranslation func ResourceInterfaceEthernetSwitchCrsIngressVlanTranslation() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/ethernet/switch/ingress-vlan-translation"), MetaId: PropId(Id), KeyComment: PropCommentRw, "customer_dei": { Type: schema.TypeString, Optional: true, Description: "Matching DEI of the customer tag.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "customer_pcp": { Type: schema.TypeString, Optional: true, Description: "Matching PCP of the customer tag.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "customer_vid": { Type: schema.TypeString, Optional: true, Description: "Matching the VLAN ID of the customer tag.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "customer_vlan_format": { Type: schema.TypeString, Optional: true, Description: "Type of frames with customer tag for which VLAN translation rule is valid.", ValidateFunc: validation.StringInSlice([]string{"any", "priority-tagged-or-tagged", "tagged", "untagged-or-tagged"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, "new_customer_vid": { Type: schema.TypeString, Optional: true, Description: "The new customer VLAN ID replaces the matching customer VLAN ID. If set to 4095 and ingress " + "VLAN translation is used, then traffic is dropped.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "new_service_vid": { Type: schema.TypeString, Optional: true, Description: "The new service VLAN ID replaces the matching service VLAN ID.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "pcp_propagation": { Type: schema.TypeString, Optional: true, Description: "Enables or disables PCP propagation.If the port type is Edge, the customer PCP is copied from " + "the service PCP.If the port type is Network, the service PCP is copied from the customer PCP.", }, "ports": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "Matching switch ports for VLAN translation rule.", }, "protocol": { Type: schema.TypeString, Optional: true, Description: "Matching Ethernet protocol (only for Ingress VLAN Translation).", ValidateFunc: validation.StringInSlice([]string{"protocols"}, false), }, "sa_learning": { Type: schema.TypeString, Optional: true, Description: "Enables or disables source MAC learning after VLAN translation (only for Ingress VLAN Translation).", }, "service_dei": { Type: schema.TypeString, Optional: true, Description: "Matching DEI of the service tag.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "service_pcp": { Type: schema.TypeString, Optional: true, Description: "Matching PCP of the service tag.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "service_vid": { Type: schema.TypeString, Optional: true, Description: "Matching VLAN ID of the service tag.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "service_vlan_format": { Type: schema.TypeString, Optional: true, Description: "Type of frames with service tag for which VLAN translation rule is valid.", ValidateFunc: validation.StringInSlice([]string{"any", "priority-tagged-or-tagged", "tagged", "untagged-or-tagged"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "swap_vids": { Type: schema.TypeString, Optional: true, Description: "", }, } return &schema.Resource{ Description: "Resource for managing CRS (Cloud Router Switch) series device properties.", CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_ethernet_switch_crs_vlan.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* REST JSON */ // https://help.mikrotik.com/docs/spaces/ROS/pages/103841835/CRS1xx+and+2xx+series+switches#CRS1xxand2xxseriesswitches-VLAN func ResourceInterfaceEthernetSwitchCrsVlan() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/ethernet/switch/vlan"), MetaId: PropId(Id), KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, "flood": { Type: schema.TypeBool, Optional: true, Description: "Enables or disables forced VLAN flooding per VLAN. If the feature is enabled, the result of " + "the destination MAC lookup in the UFDB or MFDB is ignored,and the packet is forced to flood in the VLAN.", }, "ingress_mirror": { Type: schema.TypeBool, Optional: true, Description: "Enable the ingress mirror per VLAN to support the VLAN-based mirror function.", }, "learn": { Type: schema.TypeBool, Optional: true, Description: "Enables or disables source MAC learning for VLAN.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "ports": { Type: schema.TypeString, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "Member ports of the VLAN.", }, "qos_group": { Type: schema.TypeString, Optional: true, Description: "Defined QoS group from QoS group menu.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "svl": { Type: schema.TypeBool, Optional: true, Description: "FDB lookup mode for lookup in UFDB and MFDB.\n - Shared VLAN Learning (svl) - learning/lookup is " + "based on MAC addresses - not on VLAN IDs.\n - Independent VLAN Learning (ivl) - learning/lookup is based " + "on both MAC addresses and VLAN IDs.", }, KeyVlanId: PropVlanIdRw("VLAN ID of the VLAN member entry.", true), } return &schema.Resource{ Description: "Resource for managing CRS (Cloud Router Switch) series device properties.", CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_ethernet_switch_host.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*1", "copy-to-cpu": "false", "drop": "false", "dynamic": "false", "invalid": "false", "mac-address": "00:00:00:00:00:00", "mirror": "false", "ports": "ether1", "redirect-to-cpu": "false", "switch": "switch1" } */ // https://help.mikrotik.com/docs/display/ROS/Switch+Chip+Features#SwitchChipFeatures-HostTable func ResourceInterfaceEthernetSwitchHost() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/ethernet/switch/host"), MetaId: PropId(Id), "copy_to_cpu": { Type: schema.TypeBool, Optional: true, Description: "Whether to send a frame copy to switch CPU port from a frame with matching MAC destination address " + "(matching destination or source address for CRS3xx series switches).", }, "drop": { Type: schema.TypeBool, Optional: true, Description: "Whether to drop a frame with matching MAC source address received on a certain port (matching " + "destination or source address for CRS3xx series switches).", }, KeyDynamic: PropDynamicRo, KeyInvalid: PropInvalidRo, KeyMacAddress: PropMacAddressRw("Host's MAC address.", true), "mirror": { Type: schema.TypeBool, Optional: true, Description: "Whether to send a frame copy to mirror-target port from a frame with matching MAC destination address " + "(matching destination or source address for CRS3xx series switches).", }, "ports": { Type: schema.TypeList, Required: true, Description: "Name of the interface, static MAC address can be mapped to more that one port, including switch CPU port.", Elem: &schema.Schema{ Type: schema.TypeString, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, }, "redirect_to_cpu": { Type: schema.TypeBool, Optional: true, Description: "Whether to redirect a frame to switch CPU port from a frame with matching MAC destination address " + "(matching destination or source address for CRS3xx series switches).", }, "share_vlan_learned": { Type: schema.TypeBool, Optional: true, Description: "Whether the static host MAC address lookup is used with shared-VLAN-learning (SVL) or " + "independent-VLAN-learning (IVL). The SVL mode is used for those VLAN entries that do not support IVL or IVL is " + "disabled (independent-learning=no).", }, "switch": { Type: schema.TypeString, Required: true, Description: "Name of the switch to which the MAC address is going to be assigned to.", }, KeyVlanId: PropVlanIdRw("VLAN ID for the statically added MAC address entry.", false), } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_ethernet_switch_host_test.go ================================================ package routeros import ( "testing" ) func TestAccInterfaceEthernetSwitchHost_basic(t *testing.T) { t.Log("Test skipped, The test is skipped, the resource is only available on real hardware.") } ================================================ FILE: routeros/resource_interface_ethernet_switch_port.go ================================================ package routeros import ( "context" "fmt" "regexp" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1000000", "default-vlan-id": "0", "invalid": "false", "name": "switch1-cpu", "rx-1024-1518": "0", "rx-128-255": "0", "rx-1519-max": "0", "rx-256-511": "0", "rx-512-1023": "0", "rx-64": "0", "rx-65-127": "0", "rx-align-error": "0", "rx-broadcast": "0", "rx-bytes": "0", "rx-fcs-error": "0", "rx-fragment": "0", "rx-multicast": "0", "rx-overflow": "0", "rx-pause": "0", "rx-too-long": "0", "rx-too-short": "0", "switch": "switch1", "tx-1024-1518": "0", "tx-128-255": "0", "tx-1519-max": "0", "tx-256-511": "0", "tx-512-1023": "0", "tx-64": "0", "tx-65-127": "0", "tx-broadcast": "0", "tx-bytes": "0", "tx-collision": "0", "tx-deferred": "0", "tx-excessive-collision": "0", "tx-excessive-deferred": "0", "tx-late-collision": "0", "tx-multicast": "0", "tx-multiple-collision": "0", "tx-pause": "0", "tx-single-collision": "0", "tx-too-long": "0", "tx-underrun": "0", "vlan-header": "leave-as-is", "vlan-mode": "disabled" } */ // https://help.mikrotik.com/docs/display/ROS/L3+Hardware+Offloading#L3HardwareOffloading-SwitchPortConfiguration // https://help.mikrotik.com/docs/display/ROS/Switch+Chip+Features func ResourceInterfaceEthernetSwitchPort() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/ethernet/switch/port"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("name", "rx_1024_1518", "rx_128_255", "rx_1519_max", "rx_256_511", "rx_512_1023", "rx_64", "rx_65_127", "rx_align_error", "rx_broadcast", "rx_bytes", "rx_fcs_error", "rx_fragment", "rx_multicast", "rx_overflow", "rx_pause", "rx_too_long", "rx_too_short", "tx_1024_1518", "tx_128_255", "tx_1519_max", "tx_256_511", "tx_512_1023", "tx_64", "tx_65_127", "tx_broadcast", "tx_bytes", "tx_collision", "tx_deferred", "tx_excessive_collision", "tx_excessive_deferred", "tx_late_collision", "tx_multicast", "tx_multiple_collision", "tx_pause", "tx_single_collision", "tx_too_long", "tx_underrun", "driver_tx_byte", "driver_rx_packet", "driver_rx_byte", "driver_tx_packet", "tx_carrier_sense_error", "rx_jabber", "tx_rx_65_127", "tx_rx_512_1023", "rx_unicast", "tx_fcs_error", "tx_rx_128_255", "tx_unicast", "tx_rx_1024_max", "tx_rx_256_511", "rx_error_events", "tx_rx_64", ), "default_vlan_id": { Type: schema.TypeString, Optional: true, Description: "Adds a VLAN tag with the specified VLAN ID on all untagged ingress traffic on a port, should be used " + "with ```vlan-header``` set to ```always-strip``` on a port to configure the port to be the access port. For hybrid ports " + "```default-vlan-id``` is used to tag untagged traffic. If two ports have the same ```default-vlan-id```, then VLAN tag is " + "not added since the switch chip assumes that traffic is being forwarded between access ports.", ValidateFunc: validation.StringMatch(regexp.MustCompile(`auto|\d+`), `Value must be "auto" or integer: 0..4095`), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyInvalid: PropInvalidRo, "l3_hw_offloading": { Type: schema.TypeBool, Optional: true, Description: "Level 3 hardware offloading", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "mirror_egress": { Type: schema.TypeBool, Optional: true, Description: "Whether to send egress packet copy to the `mirror-egress-target` port, only available on " + "88E6393X, 88E6191X and 88E6190 switch chips.", }, "mirror_ingress": { Type: schema.TypeBool, Optional: true, Description: "Whether to send ingress packet copy to the `mirror-ingress-target` port, only available on " + "88E6393X, 88E6191X and 88E6190 switch chips.", }, "mirror_ingress_target": { Type: schema.TypeString, Optional: true, Description: "Selects a single mirroring ingress target port, only available on 88E6393X, 88E6191X and " + "88E6190 switch chips. Mirrored packets from `mirror-ingress` will be sent to the selected port.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyName: PropName("Port name."), KeyRunning: PropRunningRo, "switch": { Type: schema.TypeString, Computed: true, Description: "Name of the switch.", }, "vlan_header": { Type: schema.TypeString, Optional: true, Description: "Sets action which is performed on the port for egress traffic.", ValidateFunc: validation.StringInSlice([]string{"add-if-missing", "always-strip", "leave-as-is"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "vlan_mode": { Type: schema.TypeString, Optional: true, Description: "Changes the VLAN lookup mechanism against the VLAN Table for ingress traffic.", ValidateFunc: validation.StringInSlice([]string{"check", "disabled", "fallback", "secure"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } resCreateUpdate := func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { item, metadata := TerraformResourceDataToMikrotik(resSchema, d) res, err := ReadItems(&ItemId{Name, d.Get("name").(string)}, metadata.Path, m.(Client)) if err != nil { // API/REST client error. ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgPatch, err)) return diag.FromErr(err) } // Resource not found. if len(*res) == 0 { d.SetId("") ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgPatch, err)) return diag.FromErr(errorNoLongerExists) } d.SetId((*res)[0].GetID(Id)) item[".id"] = d.Id() var resUrl string if m.(Client).GetTransport() == TransportREST { resUrl = "/set" } err = m.(Client).SendRequest(crudPost, &URL{Path: metadata.Path + resUrl}, item, nil) if err != nil { return diag.FromErr(err) } return ResourceRead(ctx, resSchema, d, m) } return &schema.Resource{ CreateContext: resCreateUpdate, ReadContext: DefaultRead(resSchema), UpdateContext: resCreateUpdate, DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_ethernet_switch_port_isolation.go ================================================ package routeros import ( "context" "fmt" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*1", "forwarding-override": "ether1", "invalid": "false", "name": "ether1", "switch": "switch1" } */ // https://help.mikrotik.com/docs/display/ROS/Switch+Chip+Features#SwitchChipFeatures-Portisolation func ResourceInterfaceEthernetSwitchPortIsolation() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/ethernet/switch/port-isolation"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("name"), MetaSetUnsetFields: PropSetUnsetFields("forwarding_override"), KeyInvalid: PropInvalidRo, KeyName: PropName("Port name."), "switch": { Type: schema.TypeString, Computed: true, Description: "Name of the switch.", }, "forwarding_override": { Type: schema.TypeString, Optional: true, Description: "Forces ingress traffic to be forwarded to a specific interface. Multiple interfaces can be specified by separating them with a comma.", }, } resCreateUpdate := func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { item, metadata := TerraformResourceDataToMikrotik(resSchema, d) res, err := ReadItems(&ItemId{Name, d.Get("name").(string)}, metadata.Path, m.(Client)) if err != nil { // API/REST client error. ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgPatch, err)) return diag.FromErr(err) } // Resource not found. if len(*res) == 0 { d.SetId("") ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgPatch, err)) return diag.FromErr(errorNoLongerExists) } d.SetId((*res)[0].GetID(Id)) item[".id"] = d.Id() var resUrl string if m.(Client).GetTransport() == TransportREST { resUrl = "/set" } err = m.(Client).SendRequest(crudPost, &URL{Path: metadata.Path + resUrl}, item, nil) if err != nil { return diag.FromErr(err) } return ResourceRead(ctx, resSchema, d, m) } return &schema.Resource{ CreateContext: resCreateUpdate, ReadContext: DefaultRead(resSchema), UpdateContext: resCreateUpdate, DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_ethernet_switch_port_isolation_test.go ================================================ package routeros import ( "testing" ) func TestAccInterfaceEthernetSwitchPortIsolationTest_basic(t *testing.T) { t.Log("Test skipped, The test is skipped, the resource is only available on real hardware.") } ================================================ FILE: routeros/resource_interface_ethernet_switch_port_test.go ================================================ package routeros import ( "testing" ) func TestAccInterfaceEthernetSwitchPortTest_basic(t *testing.T) { t.Log("Test skipped, The test is skipped, the resource is only available on real hardware.") } ================================================ FILE: routeros/resource_interface_ethernet_switch_rule.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* ??? */ // https://help.mikrotik.com/docs/display/ROS/Switch+Chip+Features#SwitchChipFeatures-RuleTable func ResourceInterfaceEthernetSwitchRule() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/ethernet/switch/rule"), MetaId: PropId(Id), KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, KeyInvalid: PropInvalidRo, "copy_to_cpu": { Type: schema.TypeBool, Optional: true, Description: "Whether to send a frame copy to switch CPU port from a frame with matching MAC destination address " + "(matching destination or source address for CRS3xx series switches).", }, "dst_address": { Type: schema.TypeString, Optional: true, Description: "Matching destination IP address and mask.", }, "dst_address6": { Type: schema.TypeString, Optional: true, Description: "Matching destination IPv6 address and mask.", }, "dst_mac_address": { Type: schema.TypeString, Optional: true, Description: "Matching destination MAC address and mask.", }, "dst_port": { Type: schema.TypeInt, Optional: true, Description: "Matching destination protocol port number or range.", ValidateFunc: Validation64k, }, "dscp": { Type: schema.TypeInt, Optional: true, Description: "Matching DSCP field of the packet.", ValidateFunc: validation.IntBetween(0, 63), }, "flow_label": { Type: schema.TypeInt, Optional: true, Description: "Matching IPv6 flow label.", ValidateFunc: validation.IntBetween(0, 1048575), }, "mac_protocol": { Type: schema.TypeString, Optional: true, Description: "Matching particular MAC protocol specified by protocol name or number (skips VLAN tags if any).", }, "mirror": { Type: schema.TypeBool, Optional: true, Description: "Whether to send a frame copy to mirror-target port from a frame with matching MAC destination address " + "(matching destination or source address for CRS3xx series switches).", }, "mirror_ports": { Type: schema.TypeSet, Optional: true, Description: "Selects multiple mirroring target ports, only available on 88E6393X switch chip. " + "Matched packets in the ACL rule will be copied and sent to selected ports.", Elem: &schema.Schema{ Type: schema.TypeString, DiffSuppressFunc: AlwaysPresentNotUserProvided, }}, "new_dst_ports": { Type: schema.TypeSet, Optional: true, Description: "Changes the destination port as specified, multiple ports allowed, including a switch CPU port. An empty " + "setting will drop the packet. When the parameter is not used, the packet will be accepted.", Elem: &schema.Schema{ Type: schema.TypeString, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, }, "new_vlan_id": { Type: schema.TypeInt, Optional: true, Description: "Changes the VLAN ID to the specified value or adds a new VLAN tag if one was not already present " + "(the property only applies to the Atheros8316, and 88E6393X switch chips).", ValidateFunc: validation.IntBetween(0, 4094), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "new_vlan_priority": { Type: schema.TypeInt, Optional: true, Description: "Changes the VLAN priority field (priority code point, the property only applies to Atheros8327, QCA8337 " + "and Atheros8316 switch chips).", ValidateFunc: validation.IntBetween(0, 7), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "ports": { Type: schema.TypeList, Required: true, Description: "Name of the interface on which the rule will apply on the received traffic, multiple ports are allowed.", Elem: &schema.Schema{ Type: schema.TypeString, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, }, "protocol": { Type: schema.TypeString, Optional: true, Description: "Matching particular IP protocol specified by protocol name or number.", }, "rate": { Type: schema.TypeInt, Optional: true, Description: "Sets ingress traffic limitation (bits per second) for matched traffic, can only be applied to the first 32 rule slots " + "(the property only applies to Atheros8327/QCA8337 switch chips).", ValidateFunc: validation.IntAtLeast(0), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "redirect_to_cpu": { Type: schema.TypeBool, Optional: true, Description: "Changes the destination port of a matching packet to the switch CPU.", }, "src_address": { Type: schema.TypeString, Optional: true, Description: "Matching source IP address and mask.", }, "src_address6": { Type: schema.TypeString, Optional: true, Description: "Matching source IPv6 address and mask.", }, "src_mac_address": { Type: schema.TypeString, Optional: true, Description: "Matching source MAC address and mask.", }, "src_port": { Type: schema.TypeInt, Optional: true, Description: "Matching source protocol port number or range.", ValidateFunc: Validation64k, }, "switch": { Type: schema.TypeString, Required: true, Description: "Matching switch group on which will the rule apply.", }, "traffic_class": { Type: schema.TypeInt, Optional: true, Description: "Matching IPv6 traffic class.", ValidateFunc: validation.IntBetween(0, 255), }, "vlan_header": { Type: schema.TypeString, Optional: true, Description: "Matching VLAN header, whether the VLAN header is present or not (the property only applies to the " + "Atheros8316, Atheros8327, QCA8337, 88E6393X switch chips).", ValidateFunc: validation.StringInSlice([]string{"not-present", "present"}, false), }, KeyVlanId: PropVlanIdRw("Matching VLAN ID (the property only applies to the Atheros8316, Atheros8327, QCA8337, 88E6393X switch chips).", false), "vlan_priority": { Type: schema.TypeInt, Optional: true, Description: "Matching VLAN priority (priority code point).", ValidateFunc: validation.IntBetween(0, 7), }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_ethernet_switch_rule_test.go ================================================ package routeros import ( "testing" ) func TestAccInterfaceEthernetSwitchRule_basic(t *testing.T) { t.Log("Test skipped, The test is skipped, the resource is only available on real hardware.") } ================================================ FILE: routeros/resource_interface_ethernet_switch_test.go ================================================ package routeros import ( "testing" ) func TestAccInterfaceEthernetSwitchTest_basic(t *testing.T) { t.Log("Test skipped, The test is skipped, the resource is only available on real hardware.") } ================================================ FILE: routeros/resource_interface_ethernet_switch_vlan.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*2", "disabled": "false", "invalid": "false", "ports": "ether1", "switch": "switch1", "vlan-id": "0" } */ // https://help.mikrotik.com/docs/display/ROS/Switch+Chip+Features#SwitchChipFeatures-VLANTable func ResourceInterfaceEthernetSwitchVlan() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/ethernet/switch/vlan"), MetaId: PropId(Id), KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "independent_learning": { Type: schema.TypeBool, Optional: true, Description: "Whether to use shared-VLAN-learning (SVL) or independent-VLAN-learning (IVL).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyInvalid: PropInvalidRo, "ports": { Type: schema.TypeList, Required: true, Description: "Interface member list for the respective VLAN.", Elem: &schema.Schema{ Type: schema.TypeString, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, }, "switch": { Type: schema.TypeString, Required: true, Description: "Name of the switch for which the respective VLAN entry is intended for.", }, KeyVlanId: PropVlanIdRw("The VLAN ID for certain switch port configurations.", false), } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_ethernet_switch_vlan_test.go ================================================ package routeros import ( "testing" ) func TestAccInterfaceEthernetSwitchVlan_basic(t *testing.T) { t.Log("Test skipped, The test is skipped, the resource is only available on real hardware.") } ================================================ FILE: routeros/resource_interface_ethernet_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterfaceEthernetMinVersion = "7.12" const testInterfaceEthernetAddress = "routeros_interface_ethernet.test" const testInterfaceEthernetImportAddress = "routeros_interface_ethernet.importtest" const expectedIdForInterface3 = "*3" func TestAccInterfaceEthernetTest_basic(t *testing.T) { if !testCheckMinVersion(t, testInterfaceEthernetMinVersion) { t.Logf("Test skipped, the minimum required version is %v", testInterfaceEthernetMinVersion) return } for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccInterfaceEthernetConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceEthernetAddress), resource.TestCheckResourceAttr(testInterfaceEthernetAddress, "name", "terraform"), resource.TestCheckResourceAttr(testInterfaceEthernetAddress, "mtu", "9000"), // auto_negotiation = true + advertise || auto_negotiation = false + speed // resource.TestCheckResourceAttr(testInterfaceEthernetAddress, "advertise", "10000M-full"), resource.TestCheckResourceAttr(testInterfaceEthernetAddress, "arp", "disabled"), resource.TestCheckResourceAttr(testInterfaceEthernetAddress, "auto_negotiation", "false"), resource.TestCheckResourceAttr(testInterfaceEthernetAddress, "tx_flow_control", "auto"), resource.TestCheckResourceAttr(testInterfaceEthernetAddress, "rx_flow_control", "auto"), resource.TestCheckResourceAttr(testInterfaceEthernetAddress, "mdix_enable", "false"), resource.TestCheckResourceAttr(testInterfaceEthernetAddress, "sfp_shutdown_temperature", "60"), resource.TestCheckResourceAttr(testInterfaceEthernetAddress, "speed", "10G-baseT"), // read only properties. #slave and #switch are not returned from the virtual switch // so we can add assertions. resource.TestCheckResourceAttr(testInterfaceEthernetAddress, "running", "true"), ), }, }, }) }) } } func TestAccInterfaceEthernetTest_import(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccInterfaceEthernetImportConfig(), ResourceName: testInterfaceEthernetImportAddress, ImportStateId: "*3", ImportState: true, ImportStateCheck: func(states []*terraform.InstanceState) error { if len(states) != 1 { return fmt.Errorf("more than 1 states received, only one interface expected") } state := states[0] if state.ID != expectedIdForInterface3 { return fmt.Errorf("expected id in the state don't match %v vs %v", state.ID, expectedIdForInterface3) } return nil }, }, }, }) }) } } func testAccInterfaceEthernetConfig() string { return providerConfig + ` resource "routeros_interface_ethernet" "test" { factory_name = "ether2" name = "terraform" mtu = "9000" // advertise = "10000M-full" arp = "disabled" auto_negotiation = false tx_flow_control = "auto" rx_flow_control = "auto" // full_duplex = "true" mdix_enable = false sfp_shutdown_temperature = 60 speed = "10G-baseT" } ` } func testAccInterfaceEthernetImportConfig() string { return providerConfig + ` resource "routeros_interface_ethernet" "importtest" { factory_name = "ether3" name = "ether3" speed = "1Gbps" } ` } ================================================ FILE: routeros/resource_interface_gre.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) // ResourceInterfaceGre https://wiki.mikrotik.com/wiki/Manual:Interface/Gre func ResourceInterfaceGre() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/gre"), MetaId: PropId(Id), KeyActualMtu: PropActualMtuRo, KeyAllowFastPath: PropAllowFastPathRw, KeyClampTcpMss: PropClampTcpMssRw, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, KeyDontFragment: PropDontFragmentRw, KeyDscp: PropDscpRw, KeyIpsecSecret: PropIpsecSecretRw, KeyKeepalive: PropKeepaliveRw, KeyL2Mtu: PropL2MtuRo, KeyLocalAddress: PropLocalAddressRw, KeyMtu: PropMtuRw(), KeyName: PropNameForceNewRw, KeyRemoteAddress: PropRemoteAddressRw, KeyRunning: PropRunningRo, } return &schema.Resource{ CreateContext: DefaultValidateCreate(resSchema, func(d *schema.ResourceData) diag.Diagnostics { if d.Get("allow_fast_path").(bool) && d.Get("ipsec_secret").(string) != "" { return diag.Errorf("can't enable fastpath together with ipsec") } return nil }), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultValidateUpdate(resSchema, func(d *schema.ResourceData) diag.Diagnostics { if d.Get("allow_fast_path").(bool) && d.Get("ipsec_secret").(string) != "" { return diag.Errorf("can't enable fastpath together with ipsec") } return nil }), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, SchemaVersion: 1, StateUpgraders: []schema.StateUpgrader{ { Type: ResourceInterfaceGreV0().CoreConfigSchema().ImpliedType(), Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), Version: 0, }, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_gre6.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) // ResourceInterfaceGre6 func ResourceInterfaceGre6() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/gre6"), MetaId: PropId(Id), KeyActualMtu: PropActualMtuRo, KeyClampTcpMss: PropClampTcpMssRw, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, KeyDscp: PropDscpRw, KeyIpsecSecret: PropIpsecSecretRw, KeyKeepalive: PropKeepaliveRw, KeyL2Mtu: PropL2MtuRo, KeyLocalAddress: PropLocalAddressRw, KeyMtu: PropMtuRw(), KeyName: PropNameForceNewRw, KeyRemoteAddress: PropRemoteAddressRw, KeyRunning: PropRunningRo, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_gre6_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testGre6Address = "routeros_interface_gre6.gre_v6" func TestAccInterfaceGre6Test_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/interface/gre6", "routeros_interface_gre6"), Steps: []resource.TestStep{ { Config: testAccInterfaceGre6Config(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testGre6Address), resource.TestCheckResourceAttr(testGre6Address, "name", "gre_v6"), ), }, }, }) }) } } func testAccInterfaceGre6Config() string { return providerConfig + ` resource "routeros_interface_veth" "veth_v6" { name = "veth_v6" address = ["2a02::1/64"] } resource "routeros_interface_gre6" "gre_v6" { name = "gre_v6" remote_address = "2a02::2" disabled = true depends_on = [routeros_interface_veth.veth_v6] } ` } ================================================ FILE: routeros/resource_interface_gre_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testGreAddress = "routeros_interface_gre.gre900" const testGreName = "GRE_900_TEST" func TestAccInterfaceGreTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/interface/gre", "routeros_interface_gre"), Steps: []resource.TestStep{ { Config: testAccInterfaceGreConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testGreAddress), resource.TestCheckResourceAttr(testGreAddress, "name", testGreName), ), }, }, }) }) } } func testAccInterfaceGreConfig() string { return providerConfig + ` resource "routeros_interface_gre" "gre900" { name = "GRE_900_TEST" remote_address = "127.0.0.1" disabled = true } ` } ================================================ FILE: routeros/resource_interface_gre_v0.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func ResourceInterfaceGreV0() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/gre"), MetaId: PropId(Name), KeyActualMtu: PropActualMtuRo, KeyAllowFastPath: PropAllowFastPathRw, KeyClampTcpMss: PropClampTcpMssRw, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, KeyDontFragment: PropDontFragmentRw, KeyDscp: PropDscpRw, KeyIpsecSecret: PropIpsecSecretRw, KeyKeepalive: PropKeepaliveRw, KeyL2Mtu: PropL2MtuRo, KeyLocalAddress: PropLocalAddressRw, KeyMtu: PropMtuRw(), KeyName: PropNameForceNewRw, KeyRemoteAddress: PropRemoteAddressRw, KeyRunning: PropRunningRo, }, } } ================================================ FILE: routeros/resource_interface_ipip.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) // ResourceInterfaceIPIP https://wiki.mikrotik.com/wiki/Manual:Interface/IPIP func ResourceInterfaceIPIP() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/ipip"), MetaId: PropId(Id), KeyActualMtu: PropActualMtuRo, KeyAllowFastPath: PropAllowFastPathRw, KeyClampTcpMss: PropClampTcpMssRw, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, KeyDontFragment: PropDontFragmentRw, KeyDscp: PropDscpRw, KeyIpsecSecret: PropIpsecSecretRw, KeyKeepalive: PropKeepaliveRw, KeyL2Mtu: PropL2MtuRo, KeyLocalAddress: PropLocalAddressRw, KeyMtu: PropMtuRw(), KeyName: PropName("Name of the ipip interface."), KeyRemoteAddress: PropRemoteAddressRw, KeyRunning: PropRunningRo, } return &schema.Resource{ CreateContext: DefaultValidateCreate(resSchema, func(d *schema.ResourceData) diag.Diagnostics { if d.Get("allow_fast_path").(bool) && d.Get("ipsec_secret").(string) != "" { return diag.Errorf("can't enable fastpath together with ipsec") } return nil }), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultValidateUpdate(resSchema, func(d *schema.ResourceData) diag.Diagnostics { if d.Get("allow_fast_path").(bool) && d.Get("ipsec_secret").(string) != "" { return diag.Errorf("can't enable fastpath together with ipsec") } return nil }), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_ipip_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIPIPAddress = "routeros_interface_ipip.ipip900" const testIPIPName = "IPIP_900_TEST" func TestAccInterfaceIPIPTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/interface/ipip", "routeros_interface_ipip"), Steps: []resource.TestStep{ { Config: testAccInterfaceIPIPConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIPIPAddress), resource.TestCheckResourceAttr(testIPIPAddress, "name", testIPIPName), ), }, }, }) }) } } func testAccInterfaceIPIPConfig() string { return providerConfig + ` resource "routeros_interface_ipip" "ipip900" { name = "IPIP_900_TEST" remote_address = "127.0.0.1" disabled = true } ` } ================================================ FILE: routeros/resource_interface_l2tp_client.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*3", "add-default-route": "false", "allow": "pap,chap,mschap1,mschap2", "allow-fast-path": "false", "connect-to": "1.2.3.4", "dial-on-demand": "false", "disabled": "false", "ipsec-secret": "", "keepalive-timeout": "60", "l2tp-proto-version": "l2tpv2", "l2tpv3-digest-hash": "md5", "max-mru": "1450", "max-mtu": "1450", "mrru": "disabled", "name": "l2tp-out1", "password": "bbb", "profile": "default-encryption", "running": "false", "use-ipsec": "false", "use-peer-dns": "no", "user": "aaa" } */ // https://help.mikrotik.com/docs/spaces/ROS/pages/2031631/L2TP#L2TP-L2TPClient func ResourceInterfaceL2tpClient() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/l2tp-client"), MetaId: PropId(Id), "add_default_route": { Type: schema.TypeBool, Optional: true, Description: "Whether to add L2TP remote address as a default route.", }, "allow": { Type: schema.TypeSet, Optional: true, Description: "Allowed authentication methods.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"mschap2", "mschap1", "chap", "pap"}, false), }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyAllowFastPath: PropAllowFastPathRw, "connect_to": { Type: schema.TypeString, Optional: true, Description: "Remote address of L2TP server (if the address is in VRF table, VRF should be specified)" + " `/interface l2tp-client`\n`add connect-to=192.168.88.1@vrf1 name=l2tp-out1 user=l2tp-client`.", }, KeyComment: PropCommentRw, "default_route_distance": { Type: schema.TypeInt, Optional: true, Description: "Since v6.2, sets distance value applied to auto created default route, if add-default-route " + "is also selected.", ValidateFunc: validation.IntBetween(1, 255), }, "dial_on_demand": { Type: schema.TypeBool, Optional: true, Description: "Connects only when outbound traffic is generated. If selected, then route with gateway address " + "from `10.112.112.0/24` network will be added while connection is not established.", }, KeyDisabled: PropDisabledRw, "ipsec_secret": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "Preshared key used when use-ipsec is enabled.", }, "keepalive_timeout": { Type: schema.TypeInt, Optional: true, Description: "Since v6.0rc13, tunnel keepalive timeout in seconds.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "l2tpv3_circuit_id": { Type: schema.TypeString, Optional: true, Description: "Set the virtual circuit identifier to bind the one end of the L2TPv3 control channel.", }, "l2tpv3_cookie_length": { Type: schema.TypeInt, Optional: true, Description: "Configures an L2TPv3 pseudowire static session cookie.", ValidateFunc: validation.StringInSlice([]string{"0", "4-bytes", "8-bytes"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "l2tpv3_digest_hash": { Type: schema.TypeString, Optional: true, Description: "Specifies which hash function to be used.", ValidateFunc: validation.StringInSlice([]string{"md5", "none", "sha1"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "l2tp_proto_version": { Type: schema.TypeString, Optional: true, Description: "Specify protocol version.", ValidateFunc: validation.StringInSlice([]string{"l2tpv2", "l2tpv3-ip", "l2tpv3-udp", "l2tpv"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "max_mru": { Type: schema.TypeInt, Optional: true, Description: "Maximum Receive Unit. Max packet size that L2TP interface will be able to receive without " + "packet fragmentation.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "max_mtu": { Type: schema.TypeInt, Optional: true, Description: "Maximum Transmission Unit. Max packet size that L2TP interface will be able to send without " + "packet fragmentation.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "mrru": { Type: schema.TypeString, Optional: true, Description: "Maximum packet size that can be received on the link. If a packet is bigger than tunnel MTU, " + "it will be split into multiple packets, allowing full size IP or Ethernet packets to be sent over the " + "tunnel.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyName: PropName("Descriptive name of the interface."), "password": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "Password used for authentication.", }, "profile": { Type: schema.TypeString, Optional: true, Description: "Specifies which PPP profile configuration will be used when establishing the tunnel.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "random_source_port": { Type: schema.TypeBool, Optional: true, Description: "", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyRunning: PropRunningRo, "src_address": { Type: schema.TypeString, Optional: true, Description: "Specify source address.", }, "user": { Type: schema.TypeString, Optional: true, Description: "User name used for authentication.", }, "use_ipsec": { Type: schema.TypeBool, Optional: true, Description: "When this option is enabled, dynamic IPSec peer configuration and policy (transport mode) " + "is added to encapsulate L2TP connection into IPSec tunnel. Multiple L2tp/ipsec clients behind the same " + "NAT will not work in this mode. To achieve such scenario, disable use-ipsec and set static policies " + "for clients with enabled `tunnel=yes`, `level=unique` settings.", }, "use_peer_dns": { Type: schema.TypeString, Optional: true, Description: "To use peer dns.", ValidateFunc: validation.StringInSlice([]string{"yes", "no", "exclusively"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_l2tp_client_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterfaceL2tpClient = "routeros_interface_l2tp_client.test" func TestAccInterfaceL2tpClientTest_basic(t *testing.T) { t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/interface/l2tp-client", "routeros_interface_l2tp_client"), Steps: []resource.TestStep{ { Config: testAccInterfaceL2tpClientConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceL2tpClient), resource.TestCheckResourceAttr(testInterfaceL2tpClient, "name", "l2tp-test-out"), resource.TestCheckResourceAttr(testInterfaceL2tpClient, "connect_to", "127.0.0.1"), resource.TestCheckResourceAttr(testInterfaceL2tpClient, "user", "aaa"), resource.TestCheckResourceAttr(testInterfaceL2tpClient, "password", "bbb"), ), }, }, }) }) } } func testAccInterfaceL2tpClientConfig() string { return fmt.Sprintf(`%v resource "routeros_interface_l2tp_client" "test" { name = "l2tp-test-out" connect_to = "127.0.0.1" user = "aaa" password = "bbb" } `, providerConfig) } ================================================ FILE: routeros/resource_interface_l2tp_server.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { "enabled": "false", "authentication": "mschap2,mschap1", "default-profile": "default-encryption", "mrru": "disabled", "max-mru": "1450", "keepalive-timeout": "30", "max-mtu": "1450", "use-ipsec": "false", "ipsec-secret": "", "accept-proto-version", "all", "accept-pseudowire-type": "all", "allow-fast-path": "false", "caller-id-type": "ip-address", "max-sessions": "unlimited", "one-session-per-host": "false", "l2tpv3-circuit-id": "", "l2tpv3-cookie-length", "0", "l2tpv3-digest-hash", "md5", "l2tpv3-ether-interface-list": "" } */ // https://help.mikrotik.com/docs/display/ROS/L2TP#L2TP-L2TPServer func ResourceInterfaceL2tpServer() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/l2tp-server/server"), MetaId: PropId(Id), KeyEnabled: PropEnabled("Enables/disables service."), "authentication": { Type: schema.TypeSet, Optional: true, Description: "Authentication algorithm.", DiffSuppressFunc: AlwaysPresentNotUserProvided, Elem: &schema.Schema{ Type: schema.TypeString, Default: "mschap2,mschap1", ValidateFunc: validation.StringInSlice([]string{"mschap2", "mschap1", "chap", "pap"}, false), }, }, "default_profile": { Type: schema.TypeString, Optional: true, Description: "Default profile to use.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "mrru": { Type: schema.TypeString, Optional: true, Description: "Maximum packet size that can be received on the link. If a packet is bigger than tunnel MTU, " + "it will be split into multiple packets, allowing full size IP or Ethernet packets to be sent over the " + "tunnel.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "max_mru": { Type: schema.TypeInt, Optional: true, Description: "Maximum Receive Unit.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.IntBetween(512, 18432), }, "keepalive_timeout": { Type: schema.TypeString, Optional: true, Default: "30", Description: "Sets keepalive timeout in seconds.", DiffSuppressFunc: TimeEqual, }, "max_mtu": { Type: schema.TypeInt, Optional: true, Description: "Maximum Transmission Unit.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.IntBetween(512, 18432), }, "use_ipsec": { Type: schema.TypeBool, Optional: true, Description: "When this option is enabled, dynamic IPSec peer configuration is added to suite" + "most of the L2TP road-warrior setups. When require is selected server will accept only" + "those L2TP connection attempts that were encapsulated in the IPSec tunnel.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "ipsec_secret": { Type: schema.TypeString, Optional: true, Description: "Preshared key used when use-ipsec is enabled.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "accept_proto_version" : { Type: schema.TypeString, Optional: true, Description: "Specify protocol version.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.StringInSlice([]string{"all", "l2tpv2", "l2tpv3"}, false), }, "accept_pseudowire_type": { Type: schema.TypeString, Optional: true, Description: "Set the pseudowire signaling protocol for specific pseudowire type.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.StringInSlice([]string{"all", "ether", "ppp"}, false), }, "allow_fast_path": { Type: schema.TypeBool, Optional: true, DiffSuppressFunc: AlwaysPresentNotUserProvided, Description: "To forward packets without additional processing in the Linux kernel.", }, "caller_id_type": { Type: schema.TypeString, Optional: true, Description: "If same source IP address is used for multiple clients set id type to number.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.StringInSlice([]string{"ip-address", "number"}, false), }, "max_sessions": { Type: schema.TypeString, Optional: true, Description: "Set number of needed sessions.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "one_session_per_host": { Type: schema.TypeBool, Optional: true, Description: "To allow one session per host.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "l2tpv3_circuit_id": { Type: schema.TypeString, Optional: true, Description: "Set the virtual circuit identifier to bind the one end of the L2TPv3 control channel.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "l2tpv3_cookie_length": { Type: schema.TypeString, Optional: true, Description: "Configures an L2TP pseudowire static session cookie.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.StringInSlice([]string{"0", "4-bytes", "8-bytes"}, false), }, "l2tpv3_digest_hash": { Type: schema.TypeString, Optional: true, Description: "Specifies which hash function to be used.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.StringInSlice([]string{"md5", "none", "sha1"}, false), }, "l2tpv3_ether_interface_list": { Type: schema.TypeString, Optional: true, Description: "Set your interface list for example the default ones- all, dynamic, none, static.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_l2tp_server_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterfaceL2tpServerSettings = "routeros_interface_l2tp_server.settings" func TestAccInterfaceL2tpServerTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccInterfaceL2tpServerConfig("false"), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceL2tpServerSettings), resource.TestCheckResourceAttr(testInterfaceL2tpServerSettings, "enabled", "false"), ), }, { Config: testAccInterfaceL2tpServerConfig("true"), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceL2tpServerSettings), resource.TestCheckResourceAttr(testInterfaceL2tpServerSettings, "enabled", "true"), ), }, }, }) }) } } func testAccInterfaceL2tpServerConfig(param string) string { return fmt.Sprintf(`%v resource "routeros_interface_l2tp_server" "settings" { enabled = %v } `, providerConfig, param) } ================================================ FILE: routeros/resource_interface_list.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func ResourceInterfaceList() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/list"), MetaId: PropId(Id), "builtin": { Type: schema.TypeBool, Computed: true, }, "comment": PropCommentRw, "dynamic": PropDynamicRo, "exclude": { Type: schema.TypeString, Optional: true, }, "include": { Type: schema.TypeString, Optional: true, }, "name": PropNameForceNewRw, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, SchemaVersion: 1, StateUpgraders: []schema.StateUpgrader{ { Type: ResourceInterfaceListV0().CoreConfigSchema().ImpliedType(), Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), Version: 0, }, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_list_member.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func ResourceInterfaceListMember() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/list/member"), MetaId: PropId(Id), KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "dynamic": { Type: schema.TypeBool, Computed: true, }, "id": { Type: schema.TypeString, Computed: true, }, "interface": { Type: schema.TypeString, Required: true, }, "list": { Type: schema.TypeString, Required: true, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_list_member_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterfaceListMemberAddress = "routeros_interface_list_member.test_list_member" func TestAccInterfaceListMemberTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/interface/list/member", "routeros_interface_list_member"), Steps: []resource.TestStep{ { Config: testAccInterfaceListMemberConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceListMemberAddress), resource.TestCheckResourceAttr(testInterfaceListMemberAddress, "interface", "ether1"), ), }, }, }) }) } } func testAccInterfaceListMemberConfig() string { return providerConfig + ` resource "routeros_interface_list_member" "test_list_member" { interface = "ether1" list = "list" } ` } ================================================ FILE: routeros/resource_interface_list_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterfaceListAddress = "routeros_interface_list.test_list" func TestAccInterfaceListTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/interface/list", "routeros_interface_list"), Steps: []resource.TestStep{ { Config: testAccInterfaceListConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceListAddress), resource.TestCheckResourceAttr(testInterfaceListAddress, "name", "test_list"), ), }, }, }) }) } } func testAccInterfaceListConfig() string { return providerConfig + ` resource "routeros_interface_list" "test_list" { name = "test_list" } ` } ================================================ FILE: routeros/resource_interface_list_v0.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func ResourceInterfaceListV0() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/list"), MetaId: PropId(Name), "builtin": { Type: schema.TypeBool, Computed: true, }, "comment": PropCommentRw, "dynamic": PropDynamicRo, "exclude": { Type: schema.TypeString, Optional: true, }, "include": { Type: schema.TypeString, Optional: true, }, "name": PropNameForceNewRw, }, } } ================================================ FILE: routeros/resource_interface_lte.go ================================================ package routeros import ( "context" "fmt" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*4", "allow-roaming": "true", "apn-profiles": "default", "band": "", "comment": "wan", "default-name": "lte1", "disabled": "false", "inactive": "false", "mtu": "1500", "name": "lte1", "network-mode": "3g,lte", "running": "true", "sms-read": "false" } */ // https://help.mikrotik.com/docs/display/ROS/LTE func ResourceInterfaceLte() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/lte"), MetaId: PropId(Id), "allow_roaming": { Type: schema.TypeBool, Optional: true, Description: "Enable data roaming for connecting to other countries data-providers. Not all LTE modems " + "support this feature. Some modems, that do not fully support this feature, will connect to the " + "network but will not establish an IP data connection with allow-roaming set to no.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "apn_profiles": { Type: schema.TypeString, Optional: true, Description: "Which APN profile to use for this interface.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "band": { Type: schema.TypeSet, Optional: true, Description: "LTE Frequency band used in communication " + "[LTE Bands and bandwidths](https://en.wikipedia.org/wiki/LTE_frequency_bands#Frequency_bands_and_channel_bandwidths).", Elem: &schema.Schema{ Type: schema.TypeInt, }, }, KeyComment: PropCommentRw, KeyDefaultName: PropDefaultNameRo("The default name for an interface."), KeyDisabled: PropDisabledRw, KeyInactive: PropInactiveRo, "modem_init": { Type: schema.TypeString, Optional: true, Description: "Modem init string (AT command that will be executed at modem startup).", }, KeyMtu: PropMtuRw(), KeyName: PropName("Descriptive name of the interface."), "network_mode": { Type: schema.TypeSet, Optional: true, Description: "Select/force mode for LTE interface to operate with.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"3g", "gsm", "lte", "5g"}, false), }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "nr_band": { Type: schema.TypeSet, Optional: true, Description: "5G NR Frequency band used in communication [5G NR Bands and bandwidths](https://en.wikipedia.org/wiki/5G_NR_frequency_bands).", Elem: &schema.Schema{ Type: schema.TypeInt, }, }, KeyRunning: PropRunningRo, "operator": { Type: schema.TypeInt, Optional: true, Description: "Used to lock the device to a specific operator full PLMN number is used for the lock " + "consisting of MCC+MNC. [PLMN codes](https://en.wikipedia.org/wiki/Public_land_mobile_network).", }, "pin": { Type: schema.TypeString, Optional: true, Description: "SIM Card's PIN code.", }, "sms_protocol": { Type: schema.TypeString, Optional: true, Description: "SMS functionality. `mbim`: uses MBIM driver. `at`: uses AT-Commands. `auto`: selects the " + "appropriate option depending on the modem.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "sms_read": { Type: schema.TypeBool, Optional: true, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } resCreateUpdate := func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { res, err := ReadItems(&ItemId{Name, d.Get("name").(string)}, GetMetadata(resSchema).Path, m.(Client)) if err != nil { // API/REST client error. ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgPatch, err)) return diag.FromErr(err) } // Resource not found. if len(*res) == 0 { d.SetId("") ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgPatch, err)) return diag.FromErr(errorNoLongerExists) } d.SetId((*res)[0].GetID(Id)) if diags := ResourceUpdate(ctx, resSchema, d, m); diags.HasError() { return diags } return ResourceRead(ctx, resSchema, d, m) } return &schema.Resource{ CreateContext: resCreateUpdate, ReadContext: DefaultRead(resSchema), UpdateContext: resCreateUpdate, DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_lte_apn.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "add-default-route": "true", "apn": "internet", "authentication": "none", "comment": "wan", "default": "true", "default-route-distance": "2", "ip-type": "auto", "name": "default", "passthrough-interface": "ether2", "passthrough-mac": "00:00:00:00:00:00", "use-network-apn": "true", "use-peer-dns": "false" } */ // https://help.mikrotik.com/docs/display/ROS/LTE#LTE-APNprofiles func ResourceInterfaceLteApn() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/lte/apn"), MetaId: PropId(Id), "add_default_route": { Type: schema.TypeBool, Optional: true, Description: "Whether to add a default route to forward all traffic over the LTE interface.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "apn": { Type: schema.TypeString, Optional: true, Description: "Service Provider's Access Point Name.", }, "authentication": { Type: schema.TypeString, Optional: true, Description: "Allowed protocol to use for authentication.", ValidateFunc: validation.StringInSlice([]string{"pap", "chap", "none"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyComment: PropCommentRw, KeyDefault: PropDefaultRo, "default_route_distance": { Type: schema.TypeInt, Optional: true, Description: "Sets distance value applied to auto-created default route, if add-default-route is also " + "selected. LTE route by default is with distance 2 to prefer wired routes over LTE.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "ip_type": { Type: schema.TypeString, Optional: true, Description: "Requested PDN type.", ValidateFunc: validation.StringInSlice([]string{"auto", "ipv4", "ipv4-ipv6", "ipv6"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "ipv6_interface": { Type: schema.TypeString, Optional: true, Description: "Interface on which to advertise IPv6 prefix.", }, KeyName: PropName("APN profile name"), "number": { Type: schema.TypeInt, Optional: true, Description: "APN profile number.", }, "passthrough_interface": { Type: schema.TypeString, Optional: true, Description: "Interface to passthrough IP configuration (activates passthrough).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "passthrough_mac": { Type: schema.TypeString, Optional: true, Description: "If set to auto, then will learn MAC from the first packet.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "passthrough_subnet_selection": { Type: schema.TypeString, Optional: true, Description: "`auto` selects the smallest possible subnet to be used for the passthrough interface. `p2p` " + "sets the passthrough interface subnet as `/32` and picks gateway address from `10.177.0.0/16` range. " + "The gateway address stays the same until the apn configuration is changed.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "password": { Type: schema.TypeString, Optional: true, Description: "Password used if any of the authentication protocols are active.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "use_network_apn": { Type: schema.TypeBool, Optional: true, Description: "Parameter is available starting from RouterOS v7 and used only for MBIM modems. If set to yes, " + "uses network provided APN.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "use_peer_dns": { Type: schema.TypeBool, Optional: true, Description: "If set to yes, uses DNS received from LTE interface.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "user": { Type: schema.TypeString, Optional: true, Description: "Username used if any of the authentication protocols are active.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_lte_apn_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterfaceLteApn = "routeros_interface_lte_apn.test" func TestAccInterfaceLteApnTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccInterfaceLteApnConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceLteApn), resource.TestCheckResourceAttr(testInterfaceLteApn, "name", "apn1"), resource.TestCheckResourceAttr(testInterfaceLteApn, "apn", "internet"), resource.TestCheckResourceAttr(testInterfaceLteApn, "authentication", "pap"), ), }, }, }) }) } } func testAccInterfaceLteApnConfig() string { return fmt.Sprintf(`%v resource "routeros_interface_lte_apn" "test" { name = "apn1" apn = "internet" authentication = "pap" } `, providerConfig) } ================================================ FILE: routeros/resource_interface_macvlan.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) // ResourceInterfaceMacVlan https://help.mikrotik.com/docs/display/ROS/MACVLAN func ResourceInterfaceMacVlan() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/macvlan"), MetaId: PropId(Id), KeyArp: PropArpRw, KeyArpTimeout: PropArpTimeoutRw, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, KeyInterface: PropInterfaceRw, KeyLoopProtect: PropLoopProtectRw, KeyLoopProtectDisableTime: PropLoopProtectDisableTimeRw, KeyLoopProtectSendInterval: PropLoopProtectSendIntervalRw, "loop_protect_status": { Type: schema.TypeBool, Optional: true, Description: "Loop protect status", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "mode": { Type: schema.TypeString, Optional: true, Default: "bridge", Description: "Sets MACVLAN interface mode:\n * private - does not allow communication between MACVLAN " + "instances on the same parent interface.\n * bridge - allows communication between MACVLAN instances on " + "the same parent interface.", ValidateFunc: validation.StringInSlice([]string{"private", "bridge"}, true), }, KeyMtu: PropMtuRw(), KeyRunning: PropRunningRo, KeyMacAddress: PropMacAddressRw( `Static MAC address of the interface. A randomly generated MAC address will be assigned when not specified.`, false, ), KeyL2Mtu: PropL2MtuRo, KeyName: PropNameForceNewRw, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_macvlan_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterfaceMacVlanAddress = "routeros_interface_macvlan.test" // resource introduced on 7.12.1 https://forum.mikrotik.com/viewtopic.php?t=201345 const testMinMacVlanVersion = "7.12.1" func TestAccInterfaceMacVlanTest_basic(t *testing.T) { if !testCheckMinVersion(t, testMinMacVlanVersion) { t.Skipf("Test skipped, the minimum required version is %v", testMinMacVlanVersion) return } for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/interface/macvlan", "routeros_interface_macvlan"), Steps: []resource.TestStep{ { Config: testAccInterfaceMacVlanConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceMacVlanAddress), resource.TestCheckResourceAttr(testInterfaceMacVlanAddress, "name", "macvlan1"), resource.TestCheckResourceAttr(testInterfaceMacVlanAddress, "disabled", "false"), ), }, }, }) }) } } func testAccInterfaceMacVlanConfig() string { return providerConfig + ` resource "routeros_interface_macvlan" "test" { interface = "ether1" name = "macvlan1" disabled = false } ` } ================================================ FILE: routeros/resource_interface_ovpn_server.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*1D", "comment": "comment", "client-address": "172.18.0.2", "disabled": "false", "encoding": "BF-128-CBC/SHA256", "mtu": "1500", "name": "ovpn-in1", "running": "true", "uptime": "1m25s", "user": "user1" } */ // https://help.mikrotik.com/docs/display/ROS/??? func ResourceInterfaceOpenVPNServer() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/ovpn-server"), MetaId: PropId(Id), "client_address": { Type: schema.TypeString, Computed: true, Description: "The address of the remote side.", }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "encoding": { Type: schema.TypeString, Computed: true, Description: "Encryption characteristics.", }, KeyMtu: PropL2MtuRo, KeyName: PropName("Interface name (Example: ovpn-in1)."), KeyRunning: PropRunningRo, "uptime": { Type: schema.TypeString, Computed: true, Description: "Connection uptime.", }, "user": { Type: schema.TypeString, Optional: true, Description: "User name used for authentication.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_pppoe_client.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1C", "ac-name": "", "add-default-route": "true", "allow": "pap,chap,mschap1,mschap2", "default-route-distance": "1", "dial-on-demand": "false", "disabled": "false", "interface": "ether2", "invalid": "false", "keepalive-timeout": "10", "max-mru": "auto", "max-mtu": "auto", "mrru": "disabled", "name": "pppoe-out1", "password": "", "profile": "default", "running": "false", "service-name": "", "use-peer-dns": "false", "user": "" } */ // https://help.mikrotik.com/docs/display/ROS/PPPoE#PPPoE-PPPoEClient func ResourceInterfacePPPoEClient() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/pppoe-client"), MetaId: PropId(Id), "ac_name": { Type: schema.TypeString, Optional: true, Default: "", Description: "Access Concentrator name, this may be left blank and the client will connect to any " + "access concentrator on the broadcast domain.", }, "add_default_route": { Type: schema.TypeBool, Optional: true, Default: false, Description: "Enable/Disable whether to add default route automatically.", }, "allow": { Type: schema.TypeSet, Optional: true, Computed: true, Description: "Allowed authentication methods, by default all methods are allowed.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"mschap2", "mschap1", "chap", "pap"}, false), }, }, KeyComment: PropCommentRw, "default_route_distance": { Type: schema.TypeInt, Optional: true, Default: 1, Description: "sets distance value applied to auto created default route, if add-default-route is also " + "selected.", ValidateFunc: validation.IntBetween(0, 255), }, "dial_on_demand": { Type: schema.TypeBool, Optional: true, Default: false, Description: "connects to AC only when outbound traffic is generated. If selected, then route with " + "gateway address from 10.112.112.0/24 network will be added while connection is not " + "established.", }, KeyDisabled: PropDisabledRw, "host_uniq": { Type: schema.TypeString, Optional: true, Description: "", DiffSuppressFunc: HexEqual, }, "interface": { Type: schema.TypeString, Required: true, Description: "Interface name on which client will run.", }, KeyInvalid: PropInvalidRo, "keepalive_timeout": { Type: schema.TypeInt, Optional: true, Default: 10, Description: "Sets keepalive timeout in seconds.", }, "max_mru": { Type: schema.TypeString, Optional: true, Default: "auto", Description: "Maximum Receive Unit.", }, "max_mtu": { Type: schema.TypeString, Optional: true, Default: "auto", Description: "Maximum Transmission Unit.", }, "mrru": { Type: schema.TypeString, Optional: true, Default: "disabled", Description: "Maximum packet size (512..65535 or disabled) that can be received on the link. If a packet " + "is bigger than tunnel MTU, it will be split into multiple packets, allowing full size IP or Ethernet " + "packets to be sent over the tunnel.", }, KeyName: PropName("Name of the PPPoE interface."), "password": { Type: schema.TypeString, Optional: true, Default: "", Sensitive: true, Description: "Password used to authenticate.", }, "profile": { Type: schema.TypeString, Optional: true, Default: "default", Description: "Specifies which PPP profile configuration will be used when establishing the tunnel.", }, KeyRunning: PropRunningRo, "service_name": { Type: schema.TypeString, Optional: true, Default: "", Description: "Specifies the service name set on the access concentrator, can be left blank to connect " + "to any PPPoE server.", }, "use_peer_dns": { Type: schema.TypeBool, Optional: true, Default: false, Description: "Enable/disable getting DNS settings from the peer.", }, "user": { Type: schema.TypeString, Optional: true, Default: "", Description: "Username used for authentication.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_pppoe_client_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterfacePPPoEClientAddress = "routeros_interface_pppoe_client.test" func TestAccInterfacePPPoEClientTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/interface/pppoe-client", "routeros_interface_pppoe_client"), Steps: []resource.TestStep{ { Config: testAccInterfacePPPoEClientConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfacePPPoEClientAddress), resource.TestCheckResourceAttr(testInterfacePPPoEClientAddress, "name", "PPPoE-Out"), ), }, }, }) }) } } func testAccInterfacePPPoEClientConfig() string { return providerConfig + ` resource "routeros_interface_pppoe_client" "test" { interface = "ether1" password = "StrongPass" service_name = "pppoeservice" name = "PPPoE-Out" disabled = false user = "MT-User" } ` } ================================================ FILE: routeros/resource_interface_pppoe_server.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*AD", "comment": "comment", "disabled": "false", "name": "pppoe-in1", "running": "false", "service": "", "user": "" } */ // https://help.mikrotik.com/docs/spaces/ROS/pages/2031625/PPPoE#PPPoE-PPPoEServer func ResourceInterfacePppoeServer() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/pppoe-server"), MetaId: PropId(Id), "authentication": { Type: schema.TypeSet, Optional: true, Description: "Authentication algorithm.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"mschap2", "mschap1", "chap", "pap"}, false), }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyComment: PropCommentRw, "default_profile": { Type: schema.TypeString, Optional: true, Description: "", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyDisabled: PropDisabledRw, KeyInterface: { Type: schema.TypeString, Optional: true, Description: "Interface that the clients are connected to", }, "keepalive_timeout": { Type: schema.TypeString, Optional: true, Description: "Defines the time period (in seconds) after which the router is starting to send keepalive " + "packets every second. If there is no traffic and no keepalive responses arrive for that period of time " + "(i.e. 2 * keepalive-timeout), the non responding client is proclaimed disconnected.", DiffSuppressFunc: TimeEqual, }, "max_mru": { Type: schema.TypeInt, Optional: true, Description: "Maximum Receive Unit. The optimal value is the MTU of the interface the tunnel is working " + "over reduced by 20 (so, for 1500-byte Ethernet link, set the MTU to 1480 to avoid fragmentation of packets).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "max_mtu": { Type: schema.TypeInt, Optional: true, Description: "Maximum Transmission Unit. The optimal value is the MTU of the interface the tunnel is working " + "over reduced by 20 (so, for 1500-byte Ethernet link, set the MTU to 1480 to avoid fragmentation of packets).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "max_sessions": { Type: schema.TypeInt, Optional: true, Description: "Maximum number of clients that the AC can serve. '0' = no limitations.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "mrru": { Type: schema.TypeString, Optional: true, Description: "Maximum packet size that can be received on the link. If a packet is bigger than tunnel MTU, " + "it will be split into multiple packets, allowing full size IP or Ethernet packets to be sent over the " + "tunnel.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyName: PropName(""), "one_session_per_host": { Type: schema.TypeBool, Optional: true, Description: "Allow only one session per host (determined by MAC address). If a host tries to establish " + "a new session, the old one will be closed.", }, "pppoe_over_vlan_range": { Type: schema.TypeInt, Optional: true, Description: "This setting allows a PPPoE server to operate over 802.1Q VLANs. By default, a PPPoE server " + "only accepts untagged packets on its interface. However, in scenarios where clients are on separate " + "VLANs, instead of creating multiple 802.1Q VLAN interfaces and bridging them together or configuring " + "individual PPPoE servers for each VLAN, you can specify the necessary VLANs directly in the PPPoE server " + "settings. When you specify the VLAN IDs, the PPPoE server will accept both untagged packets and 802.1Q " + "tagged packets from clients, and it will reply using the same VLAN. This setting can also be applied " + "to both CVLAN and SVLAN interfaces. For example, when the use-service-tag=yes option is used on a VLAN " + "interface, enabling QinQ setups as well. The setting supports a range of VLAN IDs, as well as individual " + "VLANs specified using comma-separated values. For example: pppoe-over-vlan-range=100-115,120,122,128-130.", }, KeyRunning: PropRunningRo, "service": { Type: schema.TypeString, Optional: true, Description: "This attribute is required in the ROS 7 version.", }, "service_name": { Type: schema.TypeString, Optional: true, Description: "The PPPoE service name. Server will accept clients which sends PADI message with service-names " + "that matches this setting or if service-name field in PADI message is not set.", }, "user": { Type: schema.TypeString, Optional: true, Description: "This attribute is required in the ROS 7 version.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_pppoe_server_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterfacePppoeServer = "routeros_interface_pppoe_server.test" func TestAccInterfacePppoeServerTest_basic(t *testing.T) { t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/interface/pppoe-server", "routeros_interface_pppoe_server"), Steps: []resource.TestStep{ { Config: testAccInterfacePppoeServerConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfacePppoeServer), resource.TestCheckResourceAttr(testInterfacePppoeServer, "comment", "comment"), resource.TestCheckResourceAttr(testInterfacePppoeServer, "disabled", "true"), resource.TestCheckResourceAttr(testInterfacePppoeServer, "name", "pppoe-in1"), resource.TestCheckResourceAttr(testInterfacePppoeServer, "user", ""), resource.TestCheckResourceAttr(testInterfacePppoeServer, "service", ""), ), }, }, }) }) } } func testAccInterfacePppoeServerConfig() string { return fmt.Sprintf(`%v resource "routeros_interface_pppoe_server" "test" { comment = "comment" disabled = true name = "pppoe-in1" user = "" service = "" } `, providerConfig) } ================================================ FILE: routeros/resource_interface_sstp_client.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "add-default-route": "false", "add-sni": "false", "authentication": "pap,chap,mschap1,mschap2", "certificate": "none", "ciphers": "aes256-sha", "connect-to": "sstp.example.com", "dial-on-demand": "false", "disabled": "false", "http-proxy": "::", "hw-crypto": "false", "keepalive-timeout": "60", "max-mru": "1500", "max-mtu": "1500", "mrru": "disabled", "name": "sstp-out1", "password": "password", "pfs": "false", "port": "443", "profile": "default-encryption", "proxy-port": "443", "running": "false", "tls-version": "any", "user": "username", "verify-server-address-from-certificate": "true", "verify-server-certificate": "false" } */ // https://help.mikrotik.com/docs/display/ROS/SSTP#SSTP-SSTPClient func ResourceInterfaceSSTPClient() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/sstp-client"), MetaId: PropId(Id), "authentication": { Type: schema.TypeSet, Optional: true, Description: "Authentication algorithm.", Elem: &schema.Schema{ Type: schema.TypeString, Default: "all", ValidateFunc: validation.StringInSlice([]string{"mschap2", "mschap1", "chap", "pap"}, false), }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyDisabled: PropDisabledRw, "add_default_route": { Type: schema.TypeBool, Optional: true, Description: "Whether to add L2TP remote address as a default route.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "default_route_distance": { Type: schema.TypeString, Optional: true, Description: "Sets distance value applied to auto created default route, if add-default-route is also selected.", DiffSuppressFunc: AlwaysPresentNotUserProvided, RequiredWith: []string{"add_default_route"}, }, "mrru": { Type: schema.TypeString, Optional: true, Description: "Maximum packet size that can be received on the link. If a packet is bigger than tunnel MTU, " + "it will be split into multiple packets, allowing full size IP or Ethernet packets to be sent over the " + "tunnel.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "proxy_port": { Type: schema.TypeString, Optional: true, Description: "Sets proxy port.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "add_sni": { Type: schema.TypeBool, Optional: true, Description: "Enables/disables service.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "dial_on_demand": { Type: schema.TypeBool, Optional: true, Description: "Connects only when outbound traffic is generated. If selected, then route with gateway address " + "from 10.112.112.0/24 network will be added while connection is not established.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyName: PropName("Descriptive name of the interface."), KeyRunning: PropRunningRo, "hw_crypto": { Type: schema.TypeBool, Computed: true, Description: "", }, "tls_version": { Type: schema.TypeString, Optional: true, Description: "Specifies which TLS versions to allow.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.StringInSlice([]string{"any", "only-1.2"}, false), }, "user": { Type: schema.TypeString, Required: true, Description: "User name used for authentication.", }, "certificate": { Type: schema.TypeString, Optional: true, Description: "Name of the certificate in use.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "http_proxy": { Type: schema.TypeString, Optional: true, Description: "Proxy address field.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "password": { Type: schema.TypeString, Required: true, Description: "Password used for authentication.", Sensitive: true, }, "verify_server_address_from_certificate": { Type: schema.TypeBool, Optional: true, Description: "SSTP client will verify server address in certificate.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "verify_server_certificate": { Type: schema.TypeBool, Optional: true, Description: "SSTP client will verify server certificate.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "ciphers": { Type: schema.TypeString, Optional: true, Description: `Allowed ciphers.`, DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateDiagFunc: ValidationMultiValInSlice([]string{"null", "aes256-sha", "aes256-gcm-sha384"}, false, false), }, "keepalive_timeout": { Type: schema.TypeString, Optional: true, Default: "60", Description: "Sets keepalive timeout in seconds.", DiffSuppressFunc: TimeEqual, }, "pfs": { Type: schema.TypeBool, Optional: true, Description: "Specifies which TLS authentication to use. With pfs=yes, TLS will use ECDHE-RSA- and DHE-RSA-. " + "For maximum security setting pfs=required will use only ECDHE.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyComment: PropCommentRw, "max_mru": { Type: schema.TypeInt, Optional: true, Description: "Maximum Receive Unit.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.IntBetween(512, 18432), }, "max_mtu": { Type: schema.TypeInt, Optional: true, Description: "Maximum Transmission Unit.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.IntBetween(512, 18432), }, "port": { Type: schema.TypeString, Optional: true, Description: "Sets port used.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "connect_to": { Type: schema.TypeString, Required: true, Description: "Remote address of the SSTP server.", }, "profile": { Type: schema.TypeString, Optional: true, Description: "Specifies which PPP profile configuration will be used when establishing the tunnel.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_sstp_server.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { "authentication": "pap,chap,mschap1,mschap2", "certificate": "none", "ciphers": "aes256-sha,aes256-gcm-sha384", "default-profile": "default", "enabled": "false", "keepalive-timeout": "60", "max-mru": "1500", "max-mtu": "1500", "mrru": "disabled", "pfs": "false", "port": "443", "tls-version": "any", "verify-client-certificate": "false" } */ // https://help.mikrotik.com/docs/display/ROS/SSTP#SSTP-SSTPServer func ResourceInterfaceSSTPServer() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/sstp-server/server"), MetaId: PropId(Id), "authentication": { Type: schema.TypeSet, Optional: true, Description: "Authentication algorithm.", DiffSuppressFunc: AlwaysPresentNotUserProvided, Elem: &schema.Schema{ Type: schema.TypeString, Default: "all", ValidateFunc: validation.StringInSlice([]string{"mschap2", "mschap1", "chap", "pap"}, false), }, }, "keepalive_timeout": { Type: schema.TypeString, Optional: true, Default: "60", Description: "Sets keepalive timeout in seconds.", DiffSuppressFunc: TimeEqual, }, "port": { Type: schema.TypeString, Optional: true, Description: "Sets port used.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "certificate": { Type: schema.TypeString, Optional: true, Description: "Name of the certificate in use.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "max_mru": { Type: schema.TypeInt, Optional: true, Description: "Maximum Receive Unit.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.IntBetween(512, 18432), }, "max_mtu": { Type: schema.TypeInt, Optional: true, Description: "Maximum Transmission Unit.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.IntBetween(512, 18432), }, "tls_version": { Type: schema.TypeString, Optional: true, Description: "Specifies which TLS versions to allow.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.StringInSlice([]string{"any", "only-1.2"}, false), }, "ciphers": { Type: schema.TypeString, Optional: true, Description: `Allowed ciphers.`, DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateDiagFunc: ValidationMultiValInSlice([]string{"null", "aes256-sha", "aes256-gcm-sha384"}, false, false), }, "verify_client_certificate": { Type: schema.TypeBool, Optional: true, DiffSuppressFunc: AlwaysPresentNotUserProvided, Description: "SSTP server will verify client certificate.", }, "mrru": { Type: schema.TypeString, Optional: true, Description: "Maximum packet size that can be received on the link. If a packet is bigger than tunnel MTU, " + "it will be split into multiple packets, allowing full size IP or Ethernet packets to be sent over the " + "tunnel.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "default_profile": { Type: schema.TypeString, Optional: true, Description: "Default profile to use.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyEnabled: PropEnabled("Enables/disables service."), "pfs": { Type: schema.TypeBool, Optional: true, Description: "Specifies which TLS authentication to use. With pfs=yes, TLS will use ECDHE-RSA- and DHE-RSA-. " + "For maximum security setting pfs=required will use only ECDHE.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_veth.go ================================================ package routeros import ( "strings" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*8", "address": "192.168.100.2/24", "comment": "comment", "disabled": "false", "gateway": "192.168.100.1", "name": "veth1", "running": "true" } */ // https://help.mikrotik.com/docs/display/ROS/Container func ResourceInterfaceVeth() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/veth"), MetaId: PropId(Id), "address": { Type: schema.TypeSet, Optional: true, Description: "Ip address.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.IsCIDR, }, }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "dhcp": { Type: schema.TypeBool, Optional: true, Description: "", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "gateway": { Type: schema.TypeString, Optional: true, Description: "Gateway IP address.", ValidateFunc: validation.IsIPv4Address, }, "gateway6": { Type: schema.TypeString, Optional: true, Description: "Gateway IPv6 address.", ValidateFunc: validation.IsIPv6Address, }, KeyMacAddress: { Type: schema.TypeString, Description: "MAC address.", Optional: true, Computed: true, ValidateFunc: validation.IsMACAddress, DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { if old != "" && d.GetRawConfig().GetAttr(k).IsNull() { return true } return strings.EqualFold(old, new) }, }, KeyName: PropName("Interface name."), KeyRunning: PropRunningRo, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_veth_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterfaceVethAddress = "routeros_interface_veth.test" func TestAccInterfaceVethTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/interface/veth", "routeros_interface_veth"), Steps: []resource.TestStep{ { Config: testAccInterfaceVethConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceVethAddress), resource.TestCheckResourceAttr(testInterfaceVethAddress, "name", "veth-test"), ), }, }, }) }) } } func testAccInterfaceVethConfig() string { return providerConfig + ` resource "routeros_interface_veth" "test" { name = "veth-test" address = ["192.168.120.2/24"] gateway = "192.168.120.1" comment = "Virtual interface" } ` } ================================================ FILE: routeros/resource_interface_vlan.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) // ResourceInterfaceVlan https://help.mikrotik.com/docs/spaces/ROS/pages/88014957/VLAN func ResourceInterfaceVlan() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/vlan"), MetaId: PropId(Id), KeyArp: PropArpRw, KeyArpTimeout: PropArpTimeoutRw, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, KeyInterface: PropInterfaceRw, KeyL2Mtu: PropL2MtuRo, KeyLoopProtect: PropLoopProtectRw, KeyLoopProtectDisableTime: PropLoopProtectDisableTimeRw, KeyLoopProtectSendInterval: PropLoopProtectSendIntervalRw, KeyLoopProtectStatus: PropLoopProtectStatusRo, KeyMacAddress: PropMacAddressRo, KeyMtu: PropMtuRw(), "mvrp": { Type: schema.TypeBool, Optional: true, //Default: false, Description: "Specifies whether this VLAN should declare its attributes through Multiple VLAN Registration Protocol (MVRP) as an applicant (available since RouterOS 7.15). " + "It can be used to register the VLAN with connected bridges that support MVRP. " + "This property only has an effect when use-service-tag is disabled.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyName: PropNameForceNewRw, KeyRunning: PropRunningRo, "use_service_tag": { Type: schema.TypeBool, Optional: true, }, KeyHwOffloaded: { Type: schema.TypeBool, Optional: true, }, KeyVlanId: PropVlanIdRw("Virtual LAN identifier or tag that is used to distinguish VLANs. Must be equal for all computers that belong to the same VLAN.", false), } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, SchemaVersion: 1, StateUpgraders: []schema.StateUpgrader{ { Type: ResourceInterfaceVlanV0().CoreConfigSchema().ImpliedType(), Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), Version: 0, }, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_vlan_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testVlanAddress = "routeros_interface_vlan.vlan900" const testVlanName = "VLAN_900_TEST" func TestAccInterfaceVlanTest(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/interface/vlan", "routeros_interface_vlan"), Steps: []resource.TestStep{ { Config: testAccInterfaceVlanConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testVlanAddress), resource.TestCheckResourceAttr(testVlanAddress, "name", testVlanName), ), }, }, }) }) } } func testAccInterfaceVlanConfig() string { return providerConfig + ` resource "routeros_interface_vlan" "vlan900" { name = "VLAN_900_TEST" vlan_id = 900 disabled = true interface = "bridge" } ` } ================================================ FILE: routeros/resource_interface_vlan_v0.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func ResourceInterfaceVlanV0() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/vlan"), MetaId: PropId(Name), KeyArp: PropArpRw, KeyArpTimeout: PropArpTimeoutRw, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, KeyInterface: PropInterfaceRw, KeyL2Mtu: PropL2MtuRo, KeyLoopProtect: PropLoopProtectRw, KeyLoopProtectDisableTime: PropLoopProtectDisableTimeRw, KeyLoopProtectSendInterval: PropLoopProtectSendIntervalRw, KeyLoopProtectStatus: PropLoopProtectStatusRo, KeyMacAddress: PropMacAddressRo, KeyMtu: PropMtuRw(), KeyName: PropNameForceNewRw, KeyRunning: PropRunningRo, "use_service_tag": { Type: schema.TypeBool, Optional: true, Default: false, }, KeyVlanId: { Type: schema.TypeInt, Required: true, }, }, } } ================================================ FILE: routeros/resource_interface_vrrp.go ================================================ package routeros import ( "regexp" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*54", "arp": "enabled", "arp-timeout": "auto", "authentication": "none", "disabled": "false", "group-master": "", "interface": "vlan55", "interval": "1s", "invalid": "false", "mac-address": "00:00:5E:00:01:01", "master": "true", "mtu": "1500", "name": "vrrp1", "on-backup": "", "on-fail": "", "on-master": "", "password": "", "preemption-mode": "true", "priority": "100", "running": "true", "sync-connection-tracking": "false", "v3-protocol": "ipv4", "version": "3", "vrid": "1" } */ // ResourceInterfaceVrrp https://help.mikrotik.com/docs/display/ROS/VRRP func ResourceInterfaceVrrp() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/vrrp"), MetaId: PropId(Id), KeyArp: PropArpRw, KeyArpTimeout: PropArpTimeoutRw, "authentication": { Type: schema.TypeString, Optional: true, Description: "Authentication method to use for VRRP advertisement packets.", ValidateFunc: validation.StringInSlice([]string{"ah", "none", "simple"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyComment: PropCommentRw, "connection_tracking_mode": { Type: schema.TypeString, Optional: true, Description: "Specifies the mode for connection tracking synchronization. This setting is only relevant " + "when `sync-connection-tracking=yes` is enabled.", ValidateFunc: validation.StringInSlice([]string{"active-active", "passive-active"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "connection_tracking_port": { Type: schema.TypeInt, Optional: true, Description: "Specifies UDP port for connection tracking synchronization. This setting is only relevant " + "when `sync-connection-tracking=yes` is enabled.", ValidateFunc: validation.IsPortNumber, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyDisabled: PropDisabledRw, "group_authority": { Type: schema.TypeString, Optional: true, Description: "Allows combining multiple VRRP interfaces to maintain the same VRRP status within the group. " + "`group_authority` was previously called `group_master`, `group_master` is kept for compatibility with " + "scripts, but if both are set only `group_authority` will be taken into account.", // This resource behavior has not changed in ROS 7.14.2. DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { if old == new { return true } if new == "none" && old == "" { return true } return AlwaysPresentNotUserProvided(k, old, new, d) }, }, "group_master": { Type: schema.TypeString, Optional: true, Description: "Allows combining multiple VRRP interfaces to maintain the same VRRP status within the group. " + "`group_authority` was previously called `group_master`, `group_master` is kept for compatibility with " + "scripts, but if both are set only `group_authority` will be taken into account.", // Maybe this is a bug, but for the 'none' value, the Mikrotik ROS 7.5 returns an empty string. // This resource behavior has not changed in ROS 7.14.2. DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { if old == new { return true } if new == "none" && old == "" { return true } return AlwaysPresentNotUserProvided(k, old, new, d) }, }, KeyInterface: PropInterfaceRw, "interval": { Type: schema.TypeString, Optional: true, Description: "VRRP update interval in seconds. Defines how often master sends advertisement packets.", ValidateFunc: validation.StringMatch(regexp.MustCompile(`^(\d+(ms|s|m)?)+$`), "expected hello interval 10ms..4m15s"), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "invalid": { Type: schema.TypeBool, Computed: true, }, KeyMacAddress: PropMacAddressRo, KeyMtu: PropL2MtuRo, KeyName: PropNameForceNewRw, "on_fail": { Type: schema.TypeString, Optional: true, Description: "Script to execute when the node fails.", }, "on_backup": { Type: schema.TypeString, Optional: true, Description: "Script to execute when the node is switched to the backup state.", }, "on_master": { Type: schema.TypeString, Optional: true, Description: "Script to execute when the node is switched to master state.", }, "password": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "Password required for authentication. Can be ignored if authentication is not used.", }, "preemption_mode": { Type: schema.TypeBool, Optional: true, Description: "Whether the master node always has the priority. When set to `no` the backup node will not " + "be elected to be a master until the current master fails, even if the backup node has higher priority " + "than the current master. This setting is ignored if the owner router becomes available", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "priority": { Type: schema.TypeInt, Optional: true, Description: "Priority of VRRP node used in Master election algorithm. A higher number means higher " + "priority. `255` is reserved for the router that owns VR IP and `0` is reserved for the Master router " + "to indicate that it is releasing responsibility.", ValidateFunc: validation.IntBetween(1, 254), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "remote_address": { Type: schema.TypeString, Optional: true, Description: "Specifies the remote address of the other VRRP router for syncing connection tracking. " + "If not set, the system autodetects the remote address via VRRP. The remote address is used only if " + "`sync_connection_tracking = true`.Sync connection tracking uses UDP port 8275.", ValidateFunc: validation.IsIPv4Address, }, "running": { Type: schema.TypeBool, Computed: true, }, "sync_connection_tracking": { Type: schema.TypeBool, Optional: true, Description: "Synchronize connection tracking entries from Master to Backup device.", }, "v3_protocol": { Type: schema.TypeString, Optional: true, Description: "A protocol that will be used by VRRPv3. Valid only if the version is 3.", ValidateFunc: validation.StringInSlice([]string{"ipv4", "ipv6"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "version": { Type: schema.TypeInt, Optional: true, Description: "Which VRRP version to use.", ValidateFunc: validation.IntBetween(2, 3), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "vrid": { Type: schema.TypeInt, Optional: true, Description: "Virtual Router identifier. Each Virtual router must have a unique id number.", ValidateFunc: validation.IntBetween(1, 255), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultValidateCreate(resSchema, func(d *schema.ResourceData) diag.Diagnostics { if d.Get("remote_address").(string) != "" && !d.Get("sync_connection_tracking").(bool) { return diag.Diagnostics{ { Severity: diag.Warning, Summary: "sync_connection_tracking not enabled", Detail: "The remote address is used only if sync_connection_tracking=true. " + "The field will be omitted in the returned response.", }, } } return nil }), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultValidateUpdate(resSchema, func(d *schema.ResourceData) diag.Diagnostics { if d.Get("remote_address").(string) != "" && !d.Get("sync_connection_tracking").(bool) { return diag.Diagnostics{ { Severity: diag.Warning, Summary: "sync_connection_tracking not enabled", Detail: "The remote address is used only if sync_connection_tracking=true. " + "The field will be omitted in the returned response.", }, } } return nil }), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, SchemaVersion: 1, StateUpgraders: []schema.StateUpgrader{ { Type: ResourceInterfaceVrrpV0().CoreConfigSchema().ImpliedType(), Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), Version: 0, }, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_vrrp_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterfaceVrrpAddress = "routeros_interface_vrrp.test_vrrp_interface" func TestAccInterfaceVrrpTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/interface/vrrp", "routeros_interface_vrrp"), Steps: []resource.TestStep{ { Config: testAccInterfaceVrrpConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceVrrpAddress), resource.TestCheckResourceAttr(testInterfaceVrrpAddress, "interface", "ether1"), ), }, }, }) }) } } func testAccInterfaceVrrpConfig() string { return providerConfig + ` resource "routeros_interface_vrrp" "test_vrrp_interface" { name = "test_vrrp_interface" interface = "ether1" } ` } ================================================ FILE: routeros/resource_interface_vrrp_v0.go ================================================ package routeros import ( "regexp" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) func ResourceInterfaceVrrpV0() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/vrrp"), MetaId: PropId(Name), KeyArp: PropArpRw, KeyArpTimeout: PropArpTimeoutRw, "authentication": { Type: schema.TypeString, Optional: true, Default: "none", Description: "Authentication method to use for VRRP advertisement packets.", ValidateFunc: validation.StringInSlice([]string{"ah", "none", "simple"}, false), }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "group_master": { Type: schema.TypeString, Optional: true, Computed: true, Description: "Allows combining multiple VRRP interfaces to maintain the same VRRP status within the group.", // Maybe this is a bug, but for the 'none' value, the Mikrotik ROS 7.5 returns an empty string. DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { if old == new { return true } if new == "none" && old == "" { return true } return false }, }, KeyInterface: PropInterfaceRw, "interval": { Type: schema.TypeString, Optional: true, Default: "1s", Description: "VRRP update interval in seconds. Defines how often master sends advertisement packets.", ValidateFunc: validation.StringMatch(regexp.MustCompile(`^(\d+(ms|s|m)?)+$`), "expected hello interval 10ms..4m15s"), }, "invalid": { Type: schema.TypeBool, Computed: true, }, KeyMacAddress: PropMacAddressRo, KeyMtu: PropMtuRw(), KeyName: PropNameForceNewRw, "on_fail": { Type: schema.TypeString, Optional: true, Description: "Script to execute when the node fails.", }, "on_backup": { Type: schema.TypeString, Optional: true, Description: "Script to execute when the node is switched to the backup state.", }, "on_master": { Type: schema.TypeString, Optional: true, Description: "Script to execute when the node is switched to master state.", }, "password": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "Password required for authentication. Can be ignored if authentication is not used.", }, "preemption_mode": { Type: schema.TypeBool, Optional: true, Default: true, Description: "Whether the master node always has the priority. When set to 'no' the backup node will not " + "be elected to be a master until the current master fails, even if the backup node has higher priority " + "than the current master. This setting is ignored if the owner router becomes available", }, "priority": { Type: schema.TypeInt, Optional: true, Default: 100, Description: "Priority of VRRP node used in Master election algorithm. A higher number means higher " + "priority. '255' is reserved for the router that owns VR IP and '0' is reserved for the Master router " + "to indicate that it is releasing responsibility.", ValidateFunc: validation.IntBetween(1, 254), }, "remote_address": { Type: schema.TypeString, Optional: true, Description: "Specifies the remote address of the other VRRP router for syncing connection tracking. " + "If not set, the system autodetects the remote address via VRRP. The remote address is used only if " + "sync-connection-tracking=yes.Sync connection tracking uses UDP port 8275.", ValidateFunc: validation.IsIPv4Address, }, "running": { Type: schema.TypeBool, Computed: true, }, "sync_connection_tracking": { Type: schema.TypeBool, Optional: true, Description: "Synchronize connection tracking entries from Master to Backup device.", }, "v3_protocol": { Type: schema.TypeString, Optional: true, Default: "ipv4", Description: "A protocol that will be used by VRRPv3. Valid only if the version is 3.", ValidateFunc: validation.StringInSlice([]string{"ipv4", "ipv6"}, false), }, "version": { Type: schema.TypeInt, Optional: true, Default: 3, Description: "Which VRRP version to use.", ValidateFunc: validation.IntBetween(2, 3), }, "vrid": { Type: schema.TypeInt, Optional: true, Default: 1, Description: "Virtual Router identifier. Each Virtual router must have a unique id number.", ValidateFunc: validation.IntBetween(1, 255), }, }, } } ================================================ FILE: routeros/resource_interface_vxlan.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*7", "allow-fast-path": "true", "arp": "enabled", "arp-timeout": "auto", "comment": "Comment", "disabled": "false", "dont-fragment": "disabled", "l2mtu": "65535", "loop-protect": "default", "loop-protect-disable-time": "5m", "loop-protect-send-interval": "5s", "loop-protect-status": "off", "mac-address": "8A:36:70:DA:F7:FA", "max-fdb-size": "4096", "mtu": "1500", "name": "vxlan1", "port": "8472", "running": "true", "vni": "1", "vrf": "main", "vteps-ip-version": "ipv4" } */ // https://help.mikrotik.com/docs/spaces/ROS/pages/100007937/VXLAN func ResourceInterfaceVxlan() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/vxlan"), MetaId: PropId(Id), "allow_fast_path": { Type: schema.TypeBool, Optional: true, Description: "Whether to allow Fast Path processing. Fragmented and flooded packets over VXLAN are redirected " + "via a slow path. Fast Path is disabled for VXLAN interface that uses IPv6 VTEP version or VRF. The setting " + "is available since RouterOS version 7.8.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyArp: PropArpRw, KeyArpTimeout: PropArpTimeoutRw, KeyComment: PropCommentRw, "checksum": { Type: schema.TypeBool, Optional: true, Description: "Setting controls whether a UDP checksum is calculated in the transmitted outer VXLAN packets:" + "\n - `no` - the UDP checksum is set to zero in transmitted outer packets. This also allows receiving " + "VXLAN packets over IPv6 that have a zero UDP checksum." + "\n - `yes` - the UDP checksum is calculated in transmitted outer packets." + "\nIf hardware offloading is used for packet transmission, this setting is ignored, and the behavior " + "defaults to sending packets with a zero UDP checksum.", }, KeyDisabled: PropDisabledRw, "dont_fragment": { Type: schema.TypeString, Optional: true, Description: "The Don't Fragment (DF) flag controls whether a packet can be broken into smaller packets, " + "called fragments, before being sent over a network. When configuring VXLAN, this setting determines " + "the presence of the DF flag on the outer IPv4 header and can control packet fragmentation if the encapsulated " + "packet exceeds the outgoing interface MTU. This setting has three options:\n * disabled - the DF flag is " + "not set on the outer IPv4 header, which means that packets can be fragmented if they are too large to " + "be sent over the outgoing interface. This also allows packet fragmentation when VXLAN uses IPv6 underlay. " + "\n * enabled - the DF flag is always set on the outer IPv4 header, which means that packets will not be fragmented " + "and will be dropped if they exceed the outgoing interface's MTU. This also avoids packet fragmentation " + "when VXLAN uses IPv6 underlay.\n * inherit - The DF flag on the outer IPv4 header is based on the inner " + "IPv4 DF flag. If the inner IPv4 header has the DF flag set, the outer IPv4 header will also have it " + "set. If the packet exceeds the outgoing interface's MTU and DF is set, it will be dropped. If the inner " + "packet is non-IP, the outer IPv4 header will not have the DF flag set and packets can be fragmented. " + "If the inner packet is IPv6, the outer IPv4 header will always set the DF flag and packets cannot be " + "fragmented. Note that when VXLAN uses IPv6 underlay, this setting does not have any effect and is treated " + "the same as disabled. The setting is available since RouterOS version 7.8.", ValidateFunc: validation.StringInSlice([]string{"disabled", "enabled", "inherit"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "group": { Type: schema.TypeString, Optional: true, Description: "When specified, a multicast group address can be used to forward broadcast, unknown-unicast, " + "and multicast traffic between VTEPs. This property requires specifying the interface setting. The interface " + "will use IGMP or MLD to join the specified multicast group, make sure to add the necessary PIM and IGMP/MDL " + "configuration. When this property is set, the vteps-ip-version automatically gets updated to the used " + "multicast IP version.", ValidateFunc: validation.StringInSlice([]string{"IPv4", "IPv6"}, false), }, "hw": { Type: schema.TypeBool, Optional: true, }, KeyHwOffloaded: PropHwOffloadedRo, "interface": { Type: schema.TypeString, Optional: true, Description: "Interface name used for multicast forwarding. This property requires specifying the group " + "setting.", }, KeyL2Mtu: PropL2MtuRo, "learning": { Type: schema.TypeBool, Optional: true, Description: "Setting controls whether inner source MAC addresses and remote VTEP IP/IPv6 addresses " + "are learned dynamically from received packets.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "local_address": { Type: schema.TypeString, Optional: true, Description: "Specifies the local source address for the VXLAN interface. If not set, one IP address of " + "the egress interface will be selected as a source address for VXLAN packets. When the property is set, " + "the vteps-ip-version automatically gets updated to the used local IP version. The setting is available " + "since RouterOS version 7.7.", }, KeyLoopProtect: PropLoopProtectRw, KeyLoopProtectDisableTime: PropLoopProtectDisableTimeRw, KeyLoopProtectSendInterval: PropLoopProtectSendIntervalRw, KeyLoopProtectStatus: PropLoopProtectStatusRo, KeyMacAddress: PropMacAddressRw("Static MAC address of the interface. A randomly generated MAC address will be assigned when not specified.", false), "max_fdb_size": { Type: schema.TypeInt, Optional: true, Description: "Limits the maximum number of MAC addresses that VXLAN can store in the forwarding database " + "(FDB).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyMtu: PropMtuRw(), KeyName: PropName("Name of the interface."), KeyRunning: PropRunningRo, "port": { Type: schema.TypeInt, Optional: true, Description: "Used UDP port number.", ValidateFunc: Validation64k, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "rem_csum": { Type: schema.TypeString, Optional: true, Description: "Changes the Remote Checksum Offload (RCO) settings for VXLAN interface. RCO is a " + "technique for eliding the inner checksum of an encapsulated datagram, allowing the outer checksum " + "to be offloaded by network driver. It does, however, involve a change to the encapsulation protocols, " + "which the receiver must also support. For this reason, it is disabled by default and setting is " + "available to ensure compatibility with systems that rely on this feature.\n" + "If hardware offloading is used, this setting is ignored, and the behavior defaults to `none`.", ValidateFunc: validation.StringInSlice([]string{"both", "none", "rx", "tx"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "ttl": { Type: schema.TypeString, Computed: true, }, "vni": { Type: schema.TypeInt, Optional: true, Description: "VXLAN Network Identifier (VNI).", ValidateFunc: validation.IntAtLeast(1), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyVrf: PropVrfRw, "vteps_ip_version": { Type: schema.TypeString, Optional: true, Description: "Used IP protocol version for statically configured VTEPs. The RouterOS VXLAN interface does " + "not support dual-stack, any configured remote VTEPs with the opposite IP version will be ignored. When " + "multicast group or local-address properties are set, the vteps-ip-version automatically gets updated " + "to the used IP version. The setting is available since RouterOS version 7.6.", ValidateFunc: validation.StringInSlice([]string{"ipv4", "ipv6"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_vxlan_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterfaceVxlan = "routeros_interface_vxlan.test" func TestAccInterfaceVxlanTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/interface/vxlan", "routeros_interface_vxlan"), Steps: []resource.TestStep{ { Config: testAccInterfaceVxlanConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceVxlan), resource.TestCheckResourceAttr(testInterfaceVxlan, "name", "vxlan1-test"), resource.TestCheckResourceAttr(testInterfaceVxlan, "vni", "10"), ), }, }, }) }) } } func testAccInterfaceVxlanConfig() string { return fmt.Sprintf(`%v resource "routeros_interface_vxlan" "test" { name = "vxlan1-test" vni = 10 } `, providerConfig) } ================================================ FILE: routeros/resource_interface_vxlan_vteps.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*3", "interface": "vxlan1", "port": "8472", "remote-ip": "::" } */ // https://help.mikrotik.com/docs/spaces/ROS/pages/100007937/VXLAN#VXLAN-Forwardingtable func ResourceInterfaceVxlanVteps() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/vxlan/vteps"), MetaId: PropId(Id), KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, KeyHwOffloaded: PropHwOffloadedRo, KeyInterface: PropInterfaceRw, "port": { Type: schema.TypeInt, Optional: true, Description: "Used UDP port number.", ValidateFunc: Validation64k, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "remote_ip": { Type: schema.TypeString, Optional: true, Description: "The IPv4 or IPv6 destination address of remote VTEP.", ValidateFunc: validation.IsIPAddress, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_vxlan_vteps_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterfaceVxlanVteps = "routeros_interface_vxlan_vteps.test" func TestAccInterfaceVxlanVtepsTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/interface/vxlan/vteps", "routeros_interface_vxlan_vteps"), Steps: []resource.TestStep{ { Config: testAccInterfaceVxlanVtepsConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceVxlanVteps), resource.TestCheckResourceAttr(testInterfaceVxlanVteps, "interface", "vxlan2-test"), resource.TestCheckResourceAttr(testInterfaceVxlanVteps, "remote_ip", "192.168.10.10"), ), }, }, }) }) } } func testAccInterfaceVxlanVtepsConfig() string { return fmt.Sprintf(`%v resource "routeros_interface_vxlan" "test-2" { name = "vxlan2-test" vni = 11 } resource "routeros_interface_vxlan_vteps" "test" { interface = routeros_interface_vxlan.test-2.name remote_ip = "192.168.10.10" } `, providerConfig) } ================================================ FILE: routeros/resource_interface_w60g.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id":"*2", "arp":"enabled", "arp-timeout":"auto", "beamforming-event":"1858", "default-scan-list":"58320,60480,62640,64800", "disabled":"false", "frequency":"60480", "isolate-stations":"false", "l2mtu":"1600", "mac-address":"48:8F:5A:F3:34:E0", "mode":"ap-bridge", "mtu":"1500", "name":"wlan60-1", "password":"password", "put-stations-in-bridge":"bridge", "region":"eu", "running":"false", "rx-mpdu-crc-err":"20354232", "rx-mpdu-crc-ok":"1401642596", "rx-ppdu":"1403448812", "ssid":"ptmp01", "tx-fw-msdu":"52", "tx-io-msdu":"0", "tx-mpdu-new":"202533008", "tx-mpdu-retry":"3570458", "tx-mpdu-total":"206103466", "tx-ppdu":"3230579961", "tx-ppdu-from-q":"2031518609", "tx-sector":"auto", "tx-sw-msdu":"202533128" } */ // https://help.mikrotik.com/docs/spaces/ROS/pages/39059501/W60G#W60G-Generalinterfaceproperties func ResourceInterfaceW60g() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/w60g"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("default_scan_list", "beamforming_event", "rx_mpdu_crc_err", "rx_mpdu_crc_ok", "rx_ppdu", "tx_fw_msdu", "tx_io_msdu", "tx_mpdu_new", "tx_mpdu_retry", "tx_mpdu_total", "tx_ppdu", "tx_ppdu_from_q", "tx_sector", "tx_sw_msdu"), KeyArp: PropArpRw, KeyArpTimeout: PropArpTimeoutRw, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "frequency": { Type: schema.TypeString, Optional: true, Description: "Frequency used in communication (Only active on bridge device).", ValidateFunc: validation.StringInSlice([]string{"58320", "60480", "62640", "64800", "66000", "auto"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "isolate_stations": { Type: schema.TypeBool, Optional: true, Description: "Don't allow communication between connected clients (from RouterOS 6.41).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyL2Mtu: PropL2MtuRw, KeyMacAddress: PropMacAddressRw("MAC address of the radio interface.", false), "mdmg_fix": { Type: schema.TypeBool, Optional: true, Description: "Experimental feature working only on wAP60Gx3 devices, providing better point to multi point " + "stability in some cases.", }, "mode": { Type: schema.TypeString, Optional: true, Description: "Operation mode.", ValidateFunc: validation.StringInSlice([]string{"ap-bridge", "bridge", "sniff", "station-bridge"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyMtu: PropMtuRw(), KeyName: PropName("Name of the interface."), "password": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "Password used for AES encryption.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "put_stations_in_bridge": { Type: schema.TypeString, Optional: true, Description: "Put newly created station device interfaces in this bridge.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "region": { Type: schema.TypeString, Optional: true, Description: "Parameter to limit frequency use.", ValidateFunc: validation.StringInSlice([]string{"asia", "australia", "canada", "china", "eu", "japan", "no-region-set", "usa"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyRunning: PropRunningRo, "scan_list": { Type: schema.TypeSet, Optional: true, Description: "Scan list to limit connectivity over frequencies in station mode.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"58320", "60480", "62640", "64800", "66000", "auto"}, false), }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "ssid": { Type: schema.TypeString, Optional: true, Description: "SSID (service set identifier) is a name that identifies wireless network (0..32 char).", ValidateFunc: validation.StringLenBetween(0, 32), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "tx_sector": { Type: schema.TypeInt, Optional: true, Description: "Disables beamforming and locks to selected radiation pattern.", ValidateFunc: validation.IntBetween(0, 63), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreateUpdate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultCreateUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_w60g_station.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id":"*8", "arp":"enabled", "arp-timeout":"auto", "beamforming-event":"3042", "disabled":"false", "mac-address":"48:8F:5A:F3:34:E0", "mtu":"1500", "name":"ptp01", "parent":"wlan60-1", "put-in-bridge":"parent", "remote-address":"48:8F:5A:47:81:9E", "running":"true" } */ // https://help.mikrotik.com/docs/spaces/ROS/pages/39059501/W60G func ResourceInterfaceW60gStation() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/w60g/station"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("beamforming_event"), KeyArp: PropArpRw, KeyArpTimeout: PropArpTimeoutRw, KeyDisabled: PropDisabledRw, KeyMacAddress: PropMacAddressRw("MAC address of the station interface.", true), KeyMtu: PropMtuRw(), KeyName: PropName("Name of the interface."), "parent": { Type: schema.TypeString, Required: true, Description: "Parent interface name.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "put_in_bridge": { Type: schema.TypeString, Optional: true, Description: "Add station device interface to specific bridge.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "remote_address": { Type: schema.TypeString, Optional: true, Description: "MAC address of bridge interface, station is connecting to.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyRunning: PropRunningRo, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_w60g_station_test.go ================================================ package routeros import ( "testing" ) func TestAccInterfaceW60gStationTest_basic(t *testing.T) { t.Log("The test is skipped, the resource is only available on real hardware.") } ================================================ FILE: routeros/resource_interface_w60g_test.go ================================================ package routeros import ( "testing" ) func TestAccInterfaceW60gTest_basic(t *testing.T) { t.Log("The test is skipped, the resource is only available on real hardware.") } ================================================ FILE: routeros/resource_interface_wireguard.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*55", "disabled": "false", "listen-port": "13231", "mtu": "1420", "name": "wireguard1", "private-key": "gLP306E2BCZBeyZ0ILrS5Ubdg4VjkFYiWkg7HpKYM10=", "public-key": "HhbyDzG6loyFAsB040GvnOcRH1Ks+M44utp6REaWPxo=", "running": "true" } */ // ResourceInterfaceWireguard https://help.mikrotik.com/docs/display/ROS/WireGuard func ResourceInterfaceWireguard() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/wireguard"), MetaId: PropId(Id), KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "listen_port": { Type: schema.TypeInt, Required: true, Description: "Port for WireGuard service to listen on for incoming sessions.", ValidateFunc: validation.IntBetween(1, 65535), }, KeyMtu: PropMtuRw(), KeyName: PropNameForceNewRw, "private_key": { Type: schema.TypeString, Computed: true, Optional: true, Sensitive: true, Description: "A base64 private key. If not specified, it will be automatically " + "generated upon interface creation.", }, "public_key": { Type: schema.TypeString, Computed: true, Description: "A base64 public key is calculated from the private key.", }, KeyRunning: PropRunningRo, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, SchemaVersion: 1, StateUpgraders: []schema.StateUpgrader{ { Type: ResourceInterfaceWireguardV0().CoreConfigSchema().ImpliedType(), Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), Version: 0, }, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_wireguard_peer.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) // ResourceInterfaceWireguardPeer https://help.mikrotik.com/docs/display/ROS/WireGuard#WireGuard-Peers func ResourceInterfaceWireguardPeer() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/wireguard/peers"), MetaId: PropId(Id), "allowed_address": { Type: schema.TypeList, Required: true, Description: "List of IP (v4 or v6) addresses with CIDR masks from which incoming traffic for this peer " + "is allowed and to which outgoing traffic for this peer is directed. The catch-all 0.0.0.0/0 may be " + "specified for matching all IPv4 addresses, and ::/0 may be specified for matching all IPv6 addresses.", Elem: &schema.Schema{ Type: schema.TypeString, // ValidateFunc: ValidationIpAddress, }, }, "client_address": { Type: schema.TypeString, Optional: true, Description: "When imported using a qr code for a client (for example, a phone), then this address for the " + "wg interface is set on that device.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "client_dns": { Type: schema.TypeString, Optional: true, Description: "Specify when using WireGuard Server as a VPN gateway for peer traffic.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "client_endpoint": { Type: schema.TypeString, Optional: true, Description: "The IP address and port number of the WireGuard Server.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "client_keepalive": { Type: schema.TypeString, Optional: true, Description: "Same as persistent-keepalive but from peer side.", ValidateFunc: ValidationTime, DiffSuppressFunc: TimeEqual, }, "client_listen_port": { Type: schema.TypeInt, Optional: true, Description: "The local port upon which this WireGuard tunnel will listen for incoming traffic from peers, " + "and the port from which it will source outgoing packets.", ValidateFunc: Validation64k, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "current_endpoint_address": { Type: schema.TypeString, Computed: true, Description: "The most recent source IP address of correctly authenticated packets from the peer.", }, "current_endpoint_port": { Type: schema.TypeInt, Computed: true, Description: "The most recent source IP port of correctly authenticated packets from the peer.", }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, "endpoint_address": { Type: schema.TypeString, Optional: true, Computed: true, Description: "An endpoint IP or hostname can be left blank to allow remote connection from any address.", }, "endpoint_port": { Type: schema.TypeString, Optional: true, Computed: true, Description: "An endpoint port can be left blank to allow remote connection from any port.", }, KeyInterface: PropInterfaceRw, "is_responder": { Type: schema.TypeBool, Optional: true, Description: "Specifies if peer is intended to be connection initiator or only responder. Should be used on " + "WireGuard devices that are used as `servers` for other devices as clients to connect to. Otherwise " + "router will all repeatedly try to connect `endpoint-address` or `current-endpoint-address` causing " + "unnecessary system logs to be written.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "last_handshake": { Type: schema.TypeString, Computed: true, Description: "Time in seconds after the last successful handshake.", }, KeyName: PropNameOptional("Name of the tunnel."), "persistent_keepalive": { Type: schema.TypeString, Optional: true, Description: "A seconds interval, between 1 and 65535 inclusive, of how often to send an authenticated " + "empty packet to the peer for the purpose of keeping a stateful firewall or NAT mapping valid " + "persistently. For example, if the interface very rarely sends traffic, but it might at anytime " + "receive traffic from a peer, and it is behind NAT, the interface might benefit from having a " + "persistent keepalive interval of 25 seconds.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: ValidationDurationBetween(1, 65535), }, "preshared_key": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "A **base64** preshared key. Optional, and may be omitted. This option adds an additional layer " + "of symmetric-key cryptography to be mixed into the already existing public-key cryptography, for " + "post-quantum resistance.", }, "private_key": { Type: schema.TypeString, Optional: true, Description: "A base64 private key. If not specified, it will be automatically generated upon interface creation.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "public_key": { Type: schema.TypeString, Required: true, Description: "The remote peer's calculated public key.", }, "rx": { Type: schema.TypeString, Computed: true, Description: "The total amount of bytes received from the peer.", }, "tx": { Type: schema.TypeString, Computed: true, Description: "The total amount of bytes transmitted to the peer.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_wireguard_peer_test.go ================================================ package routeros import ( "reflect" "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterfaceWireguardPeerMinVersion = "7.12" const testInterfaceWireguardPeerAddress = "routeros_interface_wireguard_peer.wg_peer" func TestAccInterfaceWireguardPeerTest_basic(t *testing.T) { if !testCheckMinVersion(t, testInterfaceWireguardPeerMinVersion) { t.Logf("Test skipped, the minimum required version is %v", testInterfaceWireguardPeerMinVersion) return } for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/interface/wireguard/peers", "routeros_interface_wireguard_peer"), Steps: []resource.TestStep{ { Config: testAccInterfaceWireguardPeerConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceWireguardPeerAddress), resource.TestCheckResourceAttr(testInterfaceWireguardPeerAddress, "interface", "wg1"), ), }, }, }) }) } } func testAccInterfaceWireguardPeerConfig() string { return providerConfig + ` resource "routeros_interface_wireguard_peer" "wg_peer" { allowed_address = ["1.2.3.0/30"] interface = "wg1" public_key = "QxC+CTcrDdU5+ny0+2ChUH3NegTrwoVCv53TllI5T0I=" client_keepalive = "85s" } ` } func Test_resourceInterfaceWireguardPeerAllowedIPRegexp(t *testing.T) { type args struct { ip string } tests := []struct { name string args want bool }{ { "empty", args{""}, true, }, { "10.0.0.1", args{"10.0.0.1"}, true, }, { "10.0.0.1/32", args{"10.0.0.1/32"}, true, }, { "10.0.0.1/33", args{"10.0.0.1/33"}, false, }, } re := regexp.MustCompile(`^$|^(\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(/([1-2][0-9]|3[0-2]))?)$`) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := re.MatchString(tt.args.ip); !reflect.DeepEqual(got, tt.want) { t.Errorf("Test_resourceInterfaceWireguardPeerAllowedIPRegexp() = %v, want %v", got, tt.want) } }) } } ================================================ FILE: routeros/resource_interface_wireguard_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterfaceWireguardAddress = "routeros_interface_wireguard.test_wg_interface" func TestAccInterfaceWireguardTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/interface/wireguard", "routeros_interface_wireguard"), Steps: []resource.TestStep{ { Config: testAccInterfaceWireguardConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceWireguardAddress), resource.TestCheckResourceAttr(testInterfaceWireguardAddress, "listen_port", "13231"), ), }, }, }) }) } } func testAccInterfaceWireguardConfig() string { return providerConfig + ` resource "routeros_interface_wireguard" "test_wg_interface" { name = "test_wg_interface" listen_port = "13231" } ` } ================================================ FILE: routeros/resource_interface_wireguard_v0.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) func ResourceInterfaceWireguardV0() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/wireguard"), MetaId: PropId(Name), KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "listen_port": { Type: schema.TypeInt, Required: true, Description: "Port for WireGuard service to listen on for incoming sessions.", ValidateFunc: validation.IntBetween(1, 65535), }, KeyMtu: PropMtuRw(), KeyName: PropNameForceNewRw, "private_key": { Type: schema.TypeString, Computed: true, Optional: true, Sensitive: true, Description: "A base64 private key. If not specified, it will be automatically " + "generated upon interface creation.", }, "public_key": { Type: schema.TypeString, Computed: true, Description: "A base64 public key is calculated from the private key.", }, KeyRunning: PropRunningRo, }, } } ================================================ FILE: routeros/resource_interface_wireless.go ================================================ package routeros import ( "context" "fmt" "regexp" "strings" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*7", "adaptive-noise-immunity": "none", "allow-sharedkey": "false", "ampdu-priorities": "0", "amsdu-limit": "8192", "amsdu-threshold": "8192", "antenna-gain": "3", "area": "", "arp": "enabled", "arp-timeout": "auto", "band": "2ghz-b/g/n", "basic-rates-a/g": "6Mbps", "basic-rates-b": "1Mbps", "bridge-mode": "enabled", "channel-width": "20/40mhz-XX", "compression": "false", "country": "etsi", "default-ap-tx-limit": "0", "default-authentication": "true", "default-client-tx-limit": "0", "default-forwarding": "true", "default-name": "wlan1", "disable-running-check": "false", "disabled": "true", "disconnect-timeout": "3s", "distance": "indoors", "frame-lifetime": "0", "frequency": "auto", "frequency-mode": "regulatory-domain", "frequency-offset": "0", "guard-interval": "any", "hide-ssid": "false", "ht-basic-mcs": "mcs-0,mcs-1,mcs-2,mcs-3,mcs-4,mcs-5,mcs-6,mcs-7", "ht-supported-mcs": "mcs-0,mcs-1,mcs-2,mcs-3,mcs-4,mcs-5,mcs-6,mcs-7,mcs-8,mcs-9,mcs-10,mcs-11,mcs-12,mcs-13,mcs-14,mcs-15,mcs-16,mcs-17,mcs-18,mcs-19,mcs-20,mcs-21,mcs-22,mcs-23", "hw-fragmentation-threshold": "disabled", "hw-protection-mode": "none", "hw-protection-threshold": "0", "hw-retries": "7", "installation": "indoor", "interface-type": "Atheros AR9300", "interworking-profile": "disabled", "keepalive-frames": "enabled", "l2mtu": "1600", "mac-address": "D4:01:C3:30:E2:87", "max-station-count": "2007", "mode": "ap-bridge", "mtu": "1500", "multicast-buffering": "enabled", "multicast-helper": "default", "name": "wlan1", "noise-floor-threshold": "default", "nv2-cell-radius": "30", "nv2-downlink-ratio": "50", "nv2-mode": "dynamic-downlink", "nv2-noise-floor-offset": "default", "nv2-preshared-key": "", "nv2-qos": "default", "nv2-queue-count": "2", "nv2-security": "disabled", "nv2-sync-secret": "", "on-fail-retry-time": "100ms", "preamble-mode": "both", "radio-name": "D401C330E287", "rate-selection": "advanced", "rate-set": "default", "running": "false", "rx-chains": "0,1,2", "scan-list": "default", "secondary-frequency": "", "security-profile": "default", "skip-dfs-channels": "disabled", "ssid": "MikroTik-30E287", "station-bridge-clone-mac": "00:00:00:00:00:00", "station-roaming": "disabled", "supported-rates-a/g": "6Mbps,9Mbps,12Mbps,18Mbps,24Mbps,36Mbps,48Mbps,54Mbps", "supported-rates-b": "1Mbps,2Mbps,5.5Mbps,11Mbps", "tdma-period-size": "2", "tx-chains": "0,1,2", "tx-power-mode": "default", "update-stats-interval": "disabled", "vlan-id": "1", "vlan-mode": "no-tag", "wds-cost-range": "50-150", "wds-default-bridge": "none", "wds-default-cost": "100", "wds-ignore-ssid": "false", "wds-mode": "disabled", "wireless-protocol": "802.11", "wmm-support": "disabled", "wps-mode": "push-button-virtual-only" } */ // https://help.mikrotik.com/docs/display/ROS/Wireless+Interface#WirelessInterface-Overview func ResourceInterfaceWireless() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/wireless"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields(".about", "pci_info"), MetaTransformSet: PropTransformSet("basic_rates_ag: basic-rates-a/g", "supported_rates_ag: supported-rates-a/g"), MetaSetUnsetFields: PropSetUnsetFields("secondary_frequency"), "adaptive_noise_immunity": { Type: schema.TypeString, Optional: true, Description: "This property is only effective for cards based on Atheros chipset.", ValidateFunc: validation.StringInSlice([]string{"ap-and-client-mode", "client-mode", "none"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "allow_sharedkey": { Type: schema.TypeBool, Optional: true, Description: "Allow WEP Shared Key clients to connect. Note that no authentication is done for these clients " + "(WEP Shared keys are not compared to anything) - they are just accepted at once (if access list allows " + "that).", }, "amsdu_limit": { Type: schema.TypeInt, Optional: true, Description: "Max AMSDU that device is allowed to prepare when negotiated. AMSDU aggregation may significantly " + "increase throughput especially for small frames, but may increase latency in case of packet loss due " + "to retransmission of aggregated frame. Sending and receiving AMSDUs will also increase CPU usage.", ValidateFunc: validation.IntBetween(0, 8192), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "ampdu_priorities": { Type: schema.TypeSet, Optional: true, Description: "Frame priorities for which AMPDU sending (aggregating frames and sending using block acknowledgment) " + "should get negotiated and used. Using AMPDUs will increase throughput, but may increase latency, therefore, " + "may not be desirable for real-time traffic (voice, video). Due to this, by default AMPDUs are enabled " + "only for best-effort traffic.", Elem: &schema.Schema{ Type: schema.TypeInt, ValidateFunc: validation.IntBetween(0, 7), }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "amsdu_threshold": { Type: schema.TypeInt, Optional: true, Description: "Max frame size to allow including in AMSDU.", ValidateFunc: validation.IntBetween(0, 8192), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "antenna_gain": { Type: schema.TypeInt, Optional: true, Description: "Antenna gain in dBi, used to calculate maximum transmit power according to country regulations.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "antenna_mode": { Type: schema.TypeString, Optional: true, Description: "Select antenna to use for transmitting and for receiving: `ant-a` - use only 'a'; antenna `ant-b` " + "- use only 'b'; antenna `txa-rxb` - use antenna 'a' for transmitting, antenna 'b' for receiving; `rxa-txb` - " + "use antenna 'b' for transmitting, antenna 'a' for receiving.", ValidateFunc: validation.StringInSlice([]string{"ant-a", "ant-b", "rxa-txb", "txa-rxb"}, false), }, "area": { Type: schema.TypeString, Optional: true, Description: "Identifies group of wireless networks. This value is announced by AP, and can be matched in " + " connect-list by area-prefix. This is a proprietary extension.", }, "arp": { Type: schema.TypeString, Optional: true, Description: "ARP Mode.", ValidateFunc: validation.StringInSlice([]string{"disabled", "enabled", "proxy-arp", "reply-only"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "arp_timeout": { Type: schema.TypeString, Optional: true, Description: "ARP timeout is time how long ARP record is kept in ARP table after no packets are received " + "from IP. Value auto equals to the value of arp-timeout in `/ip settings`, default is 30s.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "band": { Type: schema.TypeString, Optional: true, Description: "Defines set of used data rates, channel frequencies and widths.", ValidateFunc: validation.StringInSlice([]string{"2ghz-b", "2ghz-b/g", "2ghz-b/g/n", "2ghz-g/n", "2ghz-onlyg", "2ghz-onlyn", "5ghz-a", "5ghz-a/n", "5ghz-onlyn", "5ghz-a/n/ac", "5ghz-onlyac", "5ghz-n/ac"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "basic_rates_ag": { Type: schema.TypeSet, Optional: true, Description: "Similar to the basic-rates-b property, but used for 5ghz, 5ghz-10mhz, 5ghz-5mhz, 5ghz-turbo, " + "2.4ghz-b/g, 2.4ghz-onlyg, 2ghz-10mhz, 2ghz-5mhz and 2.4ghz-g-turbo bands.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateDiagFunc: ValidationMultiValInSlice([]string{"12Mbps", "18Mbps", "24Mbps", "36Mbps", "48Mbps", "54Mbps", "6Mbps", "9Mbps"}, false, false), }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "basic_rates_b": { Type: schema.TypeSet, Optional: true, Description: "List of basic rates, used for `2.4ghz-b`, `2.4ghz-b/g` and `2.4ghz-onlyg` bands.Client will connect " + "to AP only if it supports all basic rates announced by the AP. AP will establish WDS link only if it " + "supports all basic rates of the other AP.This property has effect only in AP modes, and when value of " + "rate-set is configured.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateDiagFunc: ValidationMultiValInSlice([]string{"1Mbps", "2Mbps", "5Mbps", "11Mbps"}, false, false), }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "bridge_mode": { Type: schema.TypeString, Optional: true, Description: "Allows to use station-bridge mode.", ValidateFunc: validation.StringInSlice([]string{"disabled", "enabled"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "burst_time": { Type: schema.TypeString, Optional: true, Description: "Time in microseconds which will be used to send data without stopping. Note that no other " + "wireless cards in that network will be able to transmit data during burst-time microseconds. This setting " + "is available only for AR5000, AR5001X, and AR5001X+ chipset based cards.", }, "channel_width": { Type: schema.TypeString, Optional: true, Description: "Use of extension channels (e.g. `C`e, `eC` etc) allows additional 20MHz extension channels and " + "if it should be located below or above the control (main) channel. Extension channel allows 802.11n " + "devices to use up to 40MHz (802.11ac up to 160MHz) of spectrum in total thus increasing max throughput. " + "Channel widths with `XX` and `XXXX` extensions automatically scan for a less crowded control channel frequency " + "based on the number of concurrent devices running in every frequency and chooses the `C` - Control channel " + "frequency automatically.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyComment: PropCommentRw, "compression": { Type: schema.TypeBool, Optional: true, Description: "Setting this property to yes will allow the use of the hardware compression. Wireless interface " + "must have support for hardware compression. Connections with devices that do not use compression will " + "still work.", }, "country": { Type: schema.TypeString, Optional: true, Description: "Limits available bands, frequencies and maximum transmit power for each frequency. Also specifies " + "default value of scan-list. Value no_country_set is an FCC compliant set of channels.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "default_ap_tx_limit": { Type: schema.TypeInt, Optional: true, Description: "This is the value of ap-tx-limit for clients that do not match any entry in the access-list. " + "0 means no limit.", }, "default_authentication": { Type: schema.TypeBool, Optional: true, Description: "For AP mode, this is the value of authentication for clients that do not match any entry in " + "the access-list. For station mode, this is the value of connect for APs that do not match any entry " + "in the connect-list.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "default_client_tx_limit": { Type: schema.TypeInt, Optional: true, Description: "This is the value of `client-tx-limit` for clients that do not match any entry in the access-list. " + "0 means no limit.", }, "default_forwarding": { Type: schema.TypeBool, Optional: true, Description: "This is the value of forwarding for clients that do not match any entry in the access-list.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "default_name": { Type: schema.TypeString, Computed: true, }, "disable_running_check": { Type: schema.TypeBool, Optional: true, Description: "When set to yes interface will always have running flag. If value is set to no', the router " + "determines whether the card is up and running - for AP one or more clients have to be registered to " + "it, for station, it should be connected to an AP.", }, KeyDisabled: PropDisabledRw, "disconnect_timeout": { Type: schema.TypeString, Optional: true, Description: "This interval is measured from third sending failure on the lowest data rate. At this point " + "`3 * (hw-retries + 1)` frame transmits on the lowest data rate had failed. During disconnect-timeout packet " + "transmission will be retried with on-fail-retry-time interval. If no frame can be transmitted successfully " + "during disconnect-timeout, the connection is closed, and this event is logged as `extensive data loss`. " + "Successful frame transmission resets this timer.", DiffSuppressFunc: TimeEqual, }, "distance": { Type: schema.TypeString, Optional: true, Description: "How long to wait for confirmation of unicast frames (ACKs) before considering transmission " + "unsuccessful, or in short ACK-Timeout. Distance value has these behaviors:\n * Dynamic - causes AP to detect " + "and use the smallest timeout that works with all connected clients.\n * Indoor - uses the default ACK timeout " + "value that the hardware chip manufacturer has set.\n * Number - uses the input value in formula: `ACK-timeout " + "= ((distance * 1000) + 299) / 300 us`\nAcknowledgments are not used in Nstreme/NV2 protocols.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "frame_lifetime": { Type: schema.TypeInt, Optional: true, Description: "Discard frames that have been queued for sending longer than frame-lifetime. By default, when " + "value of this property is 0, frames are discarded only after connection is closed.", }, "frequency": { Type: schema.TypeString, Optional: true, Description: "Channel frequency value in MHz on which AP will operate. Allowed values depend on the selected " + "band, and are restricted by country setting and wireless card capabilities. This setting has no effect " + "if interface is in any of station modes, or in wds-slave mode, or if DFS is active.Note: If using mode " + "`superchannel`.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "frequency_mode": { Type: schema.TypeString, Optional: true, Description: "Three frequency modes are available:\n * regulatory-domain - Limit available channels and maximum " + "transmit power for each channel according to the value of country\n * manual-txpower - Same as above, but " + "do not limit maximum transmit power\n *`superchannel` - Conformance Testing Mode. Allow all channels supported " + "by the card.\nList of available channels for each band can be seen in `/interface wireless` info allowed-channels. " + "This mode allows you to test wireless channels outside the default scan-list and/or regulatory domain. " + "This mode should only be used in controlled environments, or if you have special permission to use it " + "in your region. Before v4.3 this was called Custom Frequency Upgrade, or Superchannel. Since RouterOS " + "v4.3 this mode is available without special key upgrades to all installations.", ValidateFunc: validation.StringInSlice([]string{"manual-txpower", "regulatory-domain", "superchannel"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "frequency_offset": { Type: schema.TypeInt, Optional: true, Description: "Allows to specify offset if the used wireless card operates at a different frequency than " + "is shown in RouterOS, in case a frequency converter is used in the card. So if your card works at 4000MHz " + "but RouterOS shows 5000MHz, set offset to 1000MHz and it will be displayed correctly. The value is in " + "MHz and can be positive or negative.", }, "guard_interval": { Type: schema.TypeString, Optional: true, Description: "Whether to allow use of short guard interval (refer to 802.11n MCS specification to see how " + "this may affect throughput). `any` will use either short or long, depending on data rate, `long` will " + "use long.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "hide_ssid": { Type: schema.TypeBool, Optional: true, Description: "`true` - AP does not include SSID in the beacon frames, and does not reply to probe requests " + "that have broadcast SSID. `false` - AP includes SSID in the beacon frames, and replies to probe requests that " + "have broadcast SSID.This property has an effect only in AP mode. Setting it to yes can remove this network " + "from the list of wireless networks that are shown by some client software. Changing this setting does " + "not improve the security of the wireless network, because SSID is included in other frames sent by the " + "AP.", }, "ht_basic_mcs": { Type: schema.TypeSet, Optional: true, Description: "Modulation and Coding Schemes that every connecting client must support. Refer to 802.11n " + "for MCS specification.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringMatch(regexp.MustCompile(`mcs-\d+`), `ht_basic_mcs format is "mcs-[0..23]": mcs-"12"`), }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "ht_supported_mcs": { Type: schema.TypeSet, Optional: true, Description: "Modulation and Coding Schemes that this device advertises as supported. Refer to 802.11n for " + "MCS specification.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringMatch(regexp.MustCompile(`mcs-\d+`), `ht_basic_mcs format is "mcs-[0..23]": mcs-"12"`), }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "hw_fragmentation_threshold": { Type: schema.TypeString, Optional: true, Description: "Specifies maximum fragment size in bytes when transmitted over the wireless medium. 802.11 " + "standard packet (MSDU in 802.11 terminologies) fragmentation allows packets to be fragmented before " + "transmitting over a wireless medium to increase the probability of successful transmission (only fragments " + "that did not transmit correctly are retransmitted). Note that transmission of a fragmented packet is " + "less efficient than transmitting unfragmented packet because of protocol overhead and increased resource " + "usage at both - transmitting and receiving party.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "hw_protection_mode": { Type: schema.TypeString, Optional: true, Description: "Frame protection support property.", ValidateFunc: validation.StringInSlice([]string{"cts-to-self", "none", "rts-cts"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "hw_protection_threshold": { Type: schema.TypeInt, Optional: true, Description: "Frame protection support property read more >>.", ValidateFunc: Validation64k, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "hw_retries": { Type: schema.TypeInt, Optional: true, Description: "Number of times sending frame is retried without considering it a transmission failure. Data-rate " + "is decreased upon failure and the frame is sent again. Three sequential failures on the lowest supported " + "rate suspend transmission to this destination for the duration of on-fail-retry-time. After that, the " + "frame is sent again. The frame is being retransmitted until transmission success, or until the client " + "is disconnected after disconnect-timeout. The frame can be discarded during this time if frame-lifetime " + "is exceeded.", ValidateFunc: validation.IntBetween(0, 15), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "installation": { Type: schema.TypeString, Optional: true, Description: "Adjusts scan-list to use indoor, outdoor or all frequencies for the country that is set.", ValidateFunc: validation.StringInSlice([]string{"any", "indoor", "outdoor"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "interface_type": { Type: schema.TypeString, Computed: true, }, "interworking_profile": { Type: schema.TypeString, Optional: true, Description: "", ValidateFunc: validation.StringInSlice([]string{"enabled", "disabled"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "keepalive_frames": { Type: schema.TypeString, Optional: true, Description: "Applies only if wireless interface is in `mode = ap-bridge`. If a client has not communicated " + "for around 20 seconds, AP sends a `keepalive-frame`. Note, disabling the feature can lead to `ghost` " + "clients in registration-table.", ValidateFunc: validation.StringInSlice([]string{"enabled", "disabled"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyL2Mtu: PropL2MtuRw, KeyMacAddress: PropMacAddressRw("MAC address.", false), "master_interface": { Type: schema.TypeString, Optional: true, Description: "Name of wireless interface that has virtual-ap capability. Virtual AP interface will only " + "work if master interface is in ap-bridge, bridge, station or wds-slave mode. This property is only for " + "virtual AP interfaces.", }, "max_station_count": { Type: schema.TypeInt, Optional: true, Description: "Maximum number of associated clients. WDS links also count toward this limit.", ValidateFunc: validation.IntBetween(1, 2007), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "mode": { Type: schema.TypeString, Optional: true, Description: "Selection between different station and access point (AP) modes.\n * Station modes: `station` - Basic " + "station mode. Find and connect to acceptable AP. `station-wds` - Same as station, but create WDS link with " + "AP, using proprietary extension. AP configuration has to allow WDS links with this device. Note that " + "this mode does not use entries in wds. `station-pseudobridge` - Same as station, but additionally perform " + "MAC address translation of all traffic. Allows interface to be bridged. `station-pseudobridge-clone` - " + "Same as station-pseudobridge, but use station-bridge-clone-mac address to connect to AP. `station-bridge` " + "- Provides support for transparent protocol-independent L2 bridging on the station device. RouterOS " + "AP accepts clients in station-bridge mode when enabled using bridge-mode parameter. In this mode, the " + "AP maintains a forwarding table with information on which MAC addresses are reachable over which station " + "device. Only works with RouterOS APs. With station-bridge mode, it is not possible to connect to CAPsMAN " + "controlled CAP.\n * AP modes: `ap-bridge` - Basic access point mode. `bridge` - Same as ap-bridge, but limited " + "to one associated client. `wds-slave` - Same as ap-bridge, but scan for AP with the same ssid and establishes " + "WDS link. If this link is lost or cannot be established, then continue scanning. If dfs-mode is radar-detect, " + "then APs with enabled hide-ssid will not be found during scanning.\n * Special modes: `alignment-only` - Put " + "the interface in a continuous transmit mode that is used for aiming the remote antenna. `nstreme-dual-slave` " + "- allow this interface to be used in nstreme-dual setup. MAC address translation in pseudobridge modes " + "works by inspecting packets and building a table of corresponding IP and MAC addresses. All packets " + "are sent to AP with the MAC address used by pseudobridge, and MAC addresses of received packets are " + "restored from the address translation table. There is a single entry in the address translation table " + "for all non-IP packets, hence more than one host in the bridged network cannot reliably use non-IP protocols. " + "Note: Currently IPv6 doesn't work over Pseudobridge.", ValidateFunc: validation.StringInSlice([]string{"station", "station-wds", "ap-bridge", "bridge", "alignment-only", "nstreme-dual-slave", "wds-slave", "station-pseudobridge", "station-pseudobridge-clone", "station-bridge"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyMtu: PropMtuRw(), "multicast_buffering": { Type: schema.TypeString, Optional: true, Description: "For a client that has power saving, buffer multicast packets until next beacon time. A client " + "should wake up to receive a beacon, by receiving beacon it sees that there are multicast packets pending, " + "and it should wait for multicast packets to be sent.", ValidateFunc: validation.StringInSlice([]string{"disabled", "enabled"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "multicast_helper": { Type: schema.TypeString, Optional: true, Description: "When set to full, multicast packets will be sent with a unicast destination MAC address, resolving " + " multicast problem on the wireless link. This option should be enabled only on the access point, clients " + "should be configured in station-bridge mode. Available starting from v5.15.disabled - disables the helper " + "and sends multicast packets with multicast destination MAC addressesdhcp - dhcp packet mac addresses " + "are changed to unicast mac addresses prior to sending them outfull - all multicast packet mac address " + "are changed to unicast mac addresses prior to sending them outdefault - default choice that currently " + "is set to dhcp. Value can be changed in future releases.", ValidateFunc: validation.StringInSlice([]string{"default", "disabled", "full"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyName: PropName("Name of the interface."), "noise_floor_threshold": { Type: schema.TypeString, Optional: true, Description: "For advanced use only, as it can badly affect the performance of the interface. It is possible " + "to manually set noise floor threshold value. By default, it is dynamically calculated. This property " + "also affects received signal strength. This property is only effective on non-AC chips.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "nv2_cell_radius": { Type: schema.TypeInt, Optional: true, Description: "Setting affects the size of contention time slot that AP allocates for clients to initiate " + "connection and also size of time slots used for estimating distance to client. When setting is too small, " + "clients that are farther away may have trouble connecting and/or disconnect with `ranging timeout` error. " + "Although during normal operation the effect of this setting should be negligible, in order to maintain " + "maximum performance, it is advised to not increase this setting if not necessary, so AP is not reserving " + "time that is actually never used, but instead allocates it for actual data transfer.on AP: distance " + "to farthest client in kmon station: no effect.", ValidateFunc: validation.IntBetween(10, 200), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "nv2_downlink_ratio": { Type: schema.TypeInt, Optional: true, Description: "Specifies the Nv2 downlink ratio. Uplink ratio is automatically calculated from the " + "downlink-ratio value. When using dynamic-downlink mode the downlink-ratio is also used when link get " + "fully saturated. Minimum value is 20 and maximum 80.", ValidateFunc: validation.IntBetween(20, 80), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "nv2_mode": { Type: schema.TypeString, Optional: true, Description: "Specifies to use dynamic or fixed downlink/uplink ratio.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "nv2_noise_floor_offset": { Type: schema.TypeString, Optional: true, Description: "", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "nv2_preshared_key": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "Specifies preshared key to be used.", }, "nv2_qos": { Type: schema.TypeString, Optional: true, Description: "Sets the packet priority mechanism, firstly data from high priority queue is sent, then lower " + "queue priority data until 0 queue priority is reached. When link is full with high priority queue data, " + "lower priority data is not sent. Use it very carefully, setting works on APframe-priority - manual setting " + "that can be tuned with Mangle rules.default - default setting where small packets receive priority for " + "best latency.", ValidateFunc: validation.StringInSlice([]string{"default", "frame-priority"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "nv2_queue_count": { Type: schema.TypeInt, Optional: true, Description: "Specifies how many priority queues are used in Nv2 network.", ValidateFunc: validation.IntBetween(2, 8), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "nv2_security": { Type: schema.TypeString, Optional: true, Description: "Specifies Nv2 security mode.", ValidateFunc: validation.StringInSlice([]string{"disabled", "enabled"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "nv2_sync_secret": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "Specifies secret key for use in the Nv2 synchronization. Secret should match on Master " + "and Slave devices in order to establish the synced state.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "on_fail_retry_time": { Type: schema.TypeString, Optional: true, Description: "After third sending failure on the lowest data rate, wait for specified time interval before " + "retrying.", DiffSuppressFunc: TimeEqual, }, "periodic_calibration": { Type: schema.TypeString, Optional: true, Description: "Setting default enables periodic calibration if info default-periodic-calibration property " + "is enabled. Value of that property depends on the type of wireless card. This property is only effective " + "for cards based on Atheros chipset.", ValidateFunc: validation.StringInSlice([]string{"default", "disabled", "enabled"}, false), }, "periodic_calibration_interval": { Type: schema.TypeInt, Optional: true, Description: "This property is only effective for cards based on Atheros chipset.", ValidateFunc: validation.IntBetween(1, 10000), }, "preamble_mode": { Type: schema.TypeString, Optional: true, Description: "Short preamble mode is an option of 802.11b standard that reduces per-frame overhead.On AP:\n * long " + "- Do not use short preamble.\n * short - Announce short preamble capability. Do not accept connections from " + "clients that do not have this capability.\n * both - Announce short preamble capability.\nOn station:\n *long - " + "do not use short preamble.\n * short - do not connect to AP if it does not support short preamble.\n * both - " + "Use short preamble if AP supports it.", ValidateFunc: validation.StringInSlice([]string{"both", "long", "short"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "prism_cardtype": { Type: schema.TypeString, Optional: true, Description: "Specify type of the installed Prism wireless card.", ValidateFunc: validation.StringInSlice([]string{"100mW", "200mW", "30mW"}, false), }, "proprietary_extensions": { Type: schema.TypeString, Optional: true, Description: "RouterOS includes proprietary information in an information element of management frames. " + "This parameter controls how this information is included. `pre-2.9.25` - This is older method. It can interoperate " + "with newer versions of RouterOS. This method is incompatible with some clients, for example, Centrino " + "based ones. `post-2.9.25` - This uses standardized way of including vendor specific information, that is " + "compatible with newer wireless clients.", ValidateFunc: validation.StringInSlice([]string{"post-2.9.25", "pre-2.9.25"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "radio_name": { Type: schema.TypeString, Computed: true, Description: "Descriptive name of the device, that is shown in registration table entries on the remote " + "devices. This is a proprietary extension.", }, "rate_selection": { Type: schema.TypeString, Optional: true, Description: "Starting from v5.9 default value is advanced since legacy mode was inefficient.", ValidateFunc: validation.StringInSlice([]string{"advanced", "legacy"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "rate_set": { Type: schema.TypeString, Optional: true, Description: "Two options are available: `default` - default basic and supported rate sets are used. Values " + "from basic-rates and supported-rates parameters have no effect. `configured` - use values from basic-rates, " + "supported-rates, basic-mcs, mcs.", ValidateFunc: validation.StringInSlice([]string{"configured", "default"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyRunning: PropRunningRo, "rx_chains": { Type: schema.TypeSet, Optional: true, Description: "Which antennas to use for receive. In current MikroTik routers, both RX and TX chain must " + "be enabled, for the chain to be enabled.", Elem: &schema.Schema{ Type: schema.TypeInt, ValidateFunc: validation.IntBetween(0, 3), }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "scan_list": { Type: schema.TypeString, Optional: true, Description: "The default value is all channels from selected band that are supported by card and allowed " + "by the country and frequency-mode settings (this list can be seen in info). For default scan list in " + "5ghz band channels are taken with 20MHz step, in 5ghz-turbo band - with 40MHz step, for all other bands " + "- with 5MHz step. If scan-list is specified manually, then all matching channels are taken. (Example: " + "scan-list=default,5200-5245,2412-2427 - This will use the default value of scan list for current band, " + "and add to it supported frequencies from 5200-5245 or 2412-2427 range.) Since RouterOS v6.0 with Winbox " + "or Webfig, for inputting of multiple frequencies, add each frequency or range of frequencies into separate " + "multiple scan-lists. Using a comma to separate frequencies is no longer supported in Winbox/Webfig since " + "v6.0.Since RouterOS v6.35 (wireless-rep) scan-list support step feature where it is possible to manually " + "specify the scan step. Example: scan-list=5500-5600:20 will generate such scan-list values 5500,5520,5540,5560,5580,5600.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "security_profile": { Type: schema.TypeString, Optional: true, Description: "Name of profile from security-profiles.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "secondary_frequency": { Type: schema.TypeString, Optional: true, Description: "Specifies secondary channel, required to enable 80+80MHz transmission. To disable 80+80MHz " + "functionality, set secondary-frequency to `` or unset the value via CLI/GUI.", ValidateFunc: validation.StringInSlice([]string{"integer"}, false), }, "ssid": { Type: schema.TypeString, Optional: true, Description: "SSID (service set identifier) is a name that identifies wireless network.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "skip_dfs_channels": { Type: schema.TypeString, Optional: true, Description: "These values are used to skip all DFS channels or specifically skip DFS CAC channels in range " + "5600-5650MHz which detection could go up to 10min.", ValidateFunc: validation.StringInSlice([]string{"string", "10min-cac", "all", "disabled"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "station_bridge_clone_mac": { Type: schema.TypeString, Optional: true, Description: "This property has effect only in the station-pseudobridge-clone mode.Use this MAC address " + "when connection to AP. If this value is 00:00:00:00:00:00, station will initially use MAC address of " + "the wireless interface.As soon as packet with MAC address of another device needs to be transmitted, " + "station will reconnect to AP using that address.", ValidateFunc: validation.IsMACAddress, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "station_roaming": { Type: schema.TypeString, Optional: true, Description: "Station Roaming feature is available only for 802.11 wireless protocol and only for station " + "modes.", ValidateFunc: validation.StringInSlice([]string{"disabled", "enabled"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "supported_rates_ag": { Type: schema.TypeString, Optional: true, Description: "List of supported rates, used for all bands except 2ghz-b.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateDiagFunc: ValidationMultiValInSlice([]string{"12Mbps", "18Mbps", "24Mbps", "36Mbps", "48Mbps", "54Mbps", "6Mbps", "9Mbps"}, false, false), }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "supported_rates_b": { Type: schema.TypeString, Optional: true, Description: "List of supported rates, used for `2ghz-b, `2ghz-b/g` and `2ghz-b/g/n` bands. Two devices will " + "communicate only using rates that are supported by both devices. This property has effect only when " + "value of rate-set is configured.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateDiagFunc: ValidationMultiValInSlice([]string{"1Mbps", "2Mbps", "5Mbps", "11Mbps"}, false, false), }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "tdma_period_size": { Type: schema.TypeInt, Optional: true, Description: "Specifies TDMA period in milliseconds. It could help on the longer distance links, it could " + "slightly increase bandwidth, while latency is increased too.", ValidateFunc: validation.IntBetween(1, 10), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "tx_chains": { Type: schema.TypeSet, Optional: true, Description: "Which antennas to use for transmitting. In current MikroTik routers, both RX and TX chain " + "must be enabled, for the chain to be enabled.", Elem: &schema.Schema{ Type: schema.TypeInt, ValidateFunc: validation.IntBetween(0, 3), }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "tx_power": { Type: schema.TypeInt, Optional: true, ValidateFunc: validation.IntBetween(-30, 40), Description: "For 802.11ac wireless interface it's total power but for 802.11a/b/g/n it's power per chain.", }, "tx_power_mode": { Type: schema.TypeString, Optional: true, Description: "Sets up tx-power mode for wireless card `default` - use values stored in the card `all-rates-fixed` " + "- use same transmit power for all data rates. Can damage the card if transmit power is set above rated " + "value of the card for used rate. `manual-table` - define transmit power for each rate separately. Can damage " + "the card if transmit power is set above rated value of the card for used rate. `card-rates` - use transmit " + "power calculated for each rate based on value of tx-power parameter. Legacy mode only compatible with " + "currently discontinued products.", ValidateFunc: validation.StringInSlice([]string{"default", "card-rates", "all-rates-fixed", "manual-table"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "update_stats_interval": { Type: schema.TypeString, Optional: true, Description: "How often to request update of signals strength and ccq values from clients. Access to registration-table " + " also triggers update of these values.This is proprietary extension.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyVlanId: PropVlanIdRw("VLAN ID to use if doing VLAN tagging.", false), "vlan_mode": { Type: schema.TypeString, Optional: true, Description: "VLAN tagging mode specifies if traffic coming from client should get tagged (and untagged when going to client).", ValidateFunc: validation.StringInSlice([]string{"default", "no-tag", "use-service-tag", "use-tag"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "vht_basic_mcs": { Type: schema.TypeString, Optional: true, Description: "Modulation and Coding Schemes that every connecting client must support. Refer to 802.11ac " + "for MCS specification. You can set MCS interval for each of Spatial Stream\n * none - will not use selected;" + "\n * mcs0-7 - client must support MCS-0 to MCS-7;\n * mcs0-8 - client must support MCS-0 to MCS-8;" + "\n * mcs0-9 - client must support MCS-0 to MCS-9.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"none", "mcs0-7", "`mcs0-8`", "mcs0-9"}, false, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "vht_supported_mcs": { Type: schema.TypeString, Optional: true, Description: "Modulation and Coding Schemes that this device advertises as supported. Refer to 802.11ac " + "for MCS specification. You can set MCS interval for each of Spatial Stream\n * none - will not use selected; " + "\n * mcs0-7 - devices will advertise as supported MCS-0 to MCS-7;\n * mcs0-8 - devices will advertise " + "as supported MCS-0 to MCS-8;\n * mcs0-9 - devices will advertise as supported MCS-0 to MCS-9.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"none", "mcs0-7", "`mcs0-8`", "mcs0-9"}, false, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "wds_cost_range": { Type: schema.TypeString, Optional: true, Description: "Bridge port cost of WDS links are automatically adjusted, depending on measured link throughput. " + "Port cost is recalculated and adjusted every 5 seconds if it has changed by more than 10%, or if more " + "than 20 seconds have passed since the last adjustment.Setting this property to 0 disables automatic " + "cost adjustment.Automatic adjustment does not work for WDS links that are manually configured as a bridge " + "port.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "wds_default_bridge": { Type: schema.TypeString, Optional: true, Description: "When WDS link is established and status of the wds interface becomes running, it will be added " + "as a bridge port to the bridge interface specified by this property. When WDS link is lost, wds interface " + "is removed from the bridge. If wds interface is already included in a bridge setup when WDS link becomes " + "active, it will not be added to bridge specified by , and will (needs editing).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "wds_default_cost": { Type: schema.TypeInt, Optional: true, Description: "Initial bridge port cost of the WDS links.", ValidateFunc: validation.IntBetween(1, 200000000), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "wds_ignore_ssid": { Type: schema.TypeBool, Optional: true, Description: "By default, WDS link between two APs can be created only when they work on the same frequency " + "and have the same SSID value. If this property is set to yes, then SSID of the remote AP will not be " + "checked. This property has no effect on connections from clients in station-wds mode. It also does not " + "work if wds-mode is static-mesh or dynamic-mesh.", }, "wds_mode": { Type: schema.TypeString, Optional: true, Description: "Controls how WDS links with other devices (APs and clients in station-wds mode) are established.\n * disabled " + "does not allow WDS links.\n * static only allows WDS links that are manually configured in WDS.\n * dynamic also " + "allows WDS links with devices that are not configured in WDS, by creating required entries dynamically. " + "Such dynamic WDS entries are removed automatically after the connection with the other AP is lost.\n * -mesh " + "modes use different (better) method for establishing link between AP, that is not compatible with APs " + "in non-mesh mode. This method avoids one-sided WDS links that are created only by one of the two APs. " + "Such links cannot pass any data.When AP or station is establishing WDS connection with another AP, it " + "uses connect-list to check whether this connection is allowed. If station in station-wds mode is establishing " + "connection with AP, AP uses access-list to check whether this connection is allowed.If mode is station-wds, " + "then this property has no effect.", ValidateFunc: validation.StringInSlice([]string{"disabled", "dynamic", "dynamic-mesh", "static", "static-mesh"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "wireless_protocol": { Type: schema.TypeString, Optional: true, Description: "Specifies protocol used on wireless interface;\n * unspecified - protocol mode used on previous " + "RouterOS versions (v3.x, v4.x). Nstreme is enabled by old enable-nstreme setting, Nv2 configuration " + "is not possible.\n * any : on AP - regular 802.11 Access Point or Nstreme Access Point; on station - selects " + "Access Point without specific sequence, it could be changed by connect-list rules.\n * nstreme - enables " + "Nstreme protocol (the same as old enable-nstreme setting).\n * nv2 - enables Nv2 protocol.\n * nv2 nstreme : on " + "AP - uses first wireless-protocol setting, always Nv2; on station - searches for Nv2 Access Point, then " + "for Nstreme Access Point.\n * nv2 nstreme 802.11 - on AP - uses first wireless-protocol setting, always Nv2; " + "on station - searches for Nv2 Access Point, then for Nstreme Access Point, then for regular 802.11 Access " + "Point.Warning! Nv2 doesn't have support for Virtual AP.", ValidateFunc: validation.StringInSlice([]string{"802.11", "any", "nstreme", "nv2", "nv2-nstreme", "nv2-nstreme-802.11", "unspecified"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, // ----------------------- "wmm_support": { Type: schema.TypeString, Optional: true, Description: "Specifies whether to enable WMM. Only applies to bands `B` and `G`. Other bands will have it " + "enabled regardless of this setting.", ValidateFunc: validation.StringInSlice([]string{"disabled", "enabled", "required"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "wps_mode": { Type: schema.TypeString, Optional: true, Description: "WPS Server allows to connect wireless clients that support WPS to AP protected with the " + "Pre-Shared Key without specifying that key in the clients configuration.", ValidateFunc: validation.StringInSlice([]string{"disabled", "push-button", "push-button-virtual-only"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ // Interaction with mixed types of elements within a single resource. // In this case, there are physical and virtual interfaces that need to be created and deleted in different ways. CreateContext: func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { metadata := GetMetadata(resSchema) filter := buildReadFilter(map[string]interface{}{"name": d.Get("name")}) items, err := ReadItemsFiltered(filter, metadata.Path, m.(Client)) if err != nil { return diag.FromErr(err) } var diags diag.Diagnostics if items == nil || len(*items) == 0 { // No interface with the specified name was found. Adding... diags = ResourceCreate(ctx, resSchema, d, m) } else { // An interface with the specified name is found. There are two options: // it is physical and then we will update it with existing settings, // or it is virtual and needs to be imported. iface := (*items)[0] if ifType, ok := iface["interface-type"]; ok { if strings.ToLower(ifType) != "virtual" { // It's a physical interface. d.SetId(iface.GetID(Id)) diags = ResourceUpdate(ctx, resSchema, d, m) } else { diags = diag.Diagnostics{diag.Diagnostic{ Severity: diag.Error, Summary: fmt.Sprintf("A virtual interface named '%v' already exists", d.Get("name")), }} } } else { diags = diag.Diagnostics{diag.Diagnostic{ Severity: diag.Error, Summary: fmt.Sprintf("The Mikrotik resource (%v print where name=%v) does not contain "+ "'interface-type' attribute in the response", metadata.Path, d.Get("name")), }} } } if diags.HasError() { return diags } return ResourceRead(ctx, resSchema, d, m) }, ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { if strings.ToLower(d.Get("interface_type").(string)) != "virtual" { // It's a physical interface. return SystemResourceDelete(ctx, resSchema, d, m) } return ResourceDelete(ctx, resSchema, d, m) }, Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_wireless_access_list.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "allow-signal-out-of-range": "10s", "ap-tx-limit": "0", "authentication": "true", "client-tx-limit": "0", "disabled": "false", "forwarding": "true", "interface": "any", "mac-address": "00:00:00:00:00:00", "management-protection-key": "", "private-algo": "none", "private-key": "", "private-pre-shared-key": "", "signal-range": "-120..120", "time": "3h3m-5h,mon,tue,wed,thu,fri", "vlan-id": "1", "vlan-mode": "default" } */ // https://help.mikrotik.com/docs/display/ROS/ func ResourceInterfaceWirelessAccessList() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/wireless/access-list"), MetaId: PropId(Id), "allow_signal_out_of_range": { Type: schema.TypeString, Optional: true, Description: "Option which permits client's signal to be out of the range always or for some time interval.", DiffSuppressFunc: TimeEqual, }, "ap_tx_limit": { Type: schema.TypeInt, Optional: true, Description: "Limit rate of data transmission to this client. Value 0 means no limit. Value is in bits per " + "second.", }, "authentication": { Type: schema.TypeBool, Optional: true, Description: "No - Client association will always fail.yes - Use authentication procedure that is specified " + "in the security-profile of the interface.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "client_tx_limit": { Type: schema.TypeInt, Optional: true, Description: "Ask client to limit rate of data transmission. Value 0 means no limit.This is a proprietary " + "extension that is supported by RouterOS clients.Value is in bits per second.", }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "forwarding": { Type: schema.TypeBool, Optional: true, Description: "\n * false - Client cannot send frames to other station that are connected to same access point." + "\n *true - Client can send frames to other stations on the same access point.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "interface": { Type: schema.TypeString, Optional: true, Description: "Rules with interface=any are used for any wireless interface and the `interface = all` defines interface-list `all` " + "name. To make rule that applies only to one wireless interface, specify that interface as a value of " + "this property.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyMacAddress: PropMacAddressRw("Rule matches client with the specified MAC address. Value 00:00:00:00:00:00 matches always.", false), "management_protection_key": { Type: schema.TypeString, Description: "Management protection shared secret.", Optional: true, }, "private_algo": { Type: schema.TypeString, Optional: true, Description: "Only for `WEP` modes.", ValidateFunc: validation.StringInSlice([]string{"104bit-wep", "40bit-wep", "aes-ccm", "none", "tkip"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "private_key": { Type: schema.TypeString, Optional: true, Description: "Only for `WEP` modes (HEX).", }, "private_pre_shared_key": { Type: schema.TypeString, Optional: true, Description: "Used in `WPA PSK` mode.", }, "signal_range": { Type: schema.TypeString, Optional: true, Description: "Rule matches if signal strength of the station is within the range.If signal strength of the " + "station will go out of the range that is specified in the rule, access point will disconnect that station.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "time": { Type: schema.TypeString, Optional: true, Description: "Rule will match only during specified time.Station will be disconnected after specified time " + "ends. Both start and end time is expressed as time since midnight, 00:00. Rule will match only during " + "specified days of the week. Ex: \"3h3m-5h,mon,tue,wed,thu,fri\"", }, KeyVlanId: PropVlanIdRw("VLAN ID to use if doing VLAN tagging.", false), "vlan_mode": { Type: schema.TypeString, Optional: true, Description: "VLAN tagging mode specifies if traffic coming from client should get tagged (and untagged when going to client).", ValidateFunc: validation.StringInSlice([]string{"default", "no-tag", "use-service-tag", "use-tag"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_wireless_access_list_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterfaceWirelessAccessList = "routeros_interface_wireless_access_list.test" func TestAccInterfaceWirelessAccessListTest_basic(t *testing.T) { if !testCheckMaxVersion(t, testCapsManChannelMaxVersion) { t.Logf("Test skipped, the maximum supported version is %v", testCapsManChannelMaxVersion) return } // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/interface/wireless/access-list", "routeros_interface_wireless_access_list"), Steps: []resource.TestStep{ { Config: testAccInterfaceWirelessAccessListConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceWirelessAccessList), resource.TestCheckResourceAttr(testInterfaceWirelessAccessList, "signal_range", "-100..100"), resource.TestCheckResourceAttr(testInterfaceWirelessAccessList, "time", "3h3m-5h,mon,tue,wed,thu,fri"), resource.TestCheckResourceAttr(testInterfaceWirelessAccessList, "mac_address", "00:AA:BB:CC:DD:EE"), ), }, }, }) }) } } func testAccInterfaceWirelessAccessListConfig() string { return fmt.Sprintf(`%v resource "routeros_interface_wireless_access_list" "test" { signal_range = "-100..100" time = "3h3m-5h,mon,tue,wed,thu,fri" mac_address = "00:AA:BB:CC:DD:EE" } `, providerConfig) } ================================================ FILE: routeros/resource_interface_wireless_cap.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".about": "", "bridge": "bridge1", "caps-man-addresses": "", "caps-man-certificate-common-names": "", "caps-man-names": "", "certificate": "CAP-000000000000", "discovery-interfaces": "bridge1", "enabled": "true", "interfaces": "wlan1,wlan2", "lock-to-caps-man": "false", "requested-certificate": "CAP-000000000000", "static-virtual": "false" } */ // https://help.mikrotik.com/docs/pages/viewpage.action?pageId=1409149#APController(CAPsMAN)-CAPtoCAPsMANConnection func ResourceInterfaceWirelessCap() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/wireless/cap"), MetaId: PropId(Name), MetaSkipFields: PropSkipFields(".about"), "bridge": { Type: schema.TypeString, Optional: true, Default: "none", Description: "Bridge interface to add the interface as a bridge port.", }, "caps_man_addresses": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.IsIPAddress, }, Description: "List of Manager IP addresses that CAP will attempt to contact during discovery.", }, "caps_man_certificate_common_names": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "List of manager certificate common names that CAP will connect to.", }, "caps_man_names": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "An ordered list of CAPs Manager names that the CAP will connect to.", }, "certificate": { Type: schema.TypeString, Optional: true, Default: "none", Description: "Certificate to use for authentication.", }, "discovery_interfaces": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "List of interfaces over which CAP should attempt to discover CAPs Manager.", }, KeyEnabled: PropEnabled("Disable or enable the CAP functionality."), "interfaces": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "List of interfaces managed by CAPs Manager.", }, "lock_to_caps_man": { Type: schema.TypeBool, Optional: true, Description: "Lock CAP to the first CAPsMAN it connects to.", }, "locked_caps_man_common_name": { Type: schema.TypeString, Computed: true, Description: "Common name of the CAPsMAN that the CAP is locked to.", }, "requested_certificate": { Type: schema.TypeString, Computed: true, Description: "Requested certificate.", }, "static_virtual": { Type: schema.TypeBool, Optional: true, Description: "An option that creates static virtual interfaces.", }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_wireless_connect_list.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* iw-asra=any iw-esr=any iw-hessid=00:00:00:00:00:00 iw-hotspot20=any iw-hotspot20-dgaf=any iw-internet=any iw-ipv4-availability=any iw-ipv6-availability=any iw-network-type=wildcard iw-roaming-ois="" iw-venue=any iw-uesa=any */ // https://help.mikrotik.com/docs/spaces/ROS/pages/8978446/Wireless+Interface#WirelessInterface-ConnectList // https://help.mikrotik.com/docs/spaces/ROS/pages/7962628/Interworking+Profiles func ResourceInterfaceWirelessConnectList() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/wireless/connect-list"), MetaId: PropId(Id), MetaTransformSet: PropTransformSet("three_gpp:3gpp"), "three_gpp": { Type: schema.TypeString, Optional: true, Description: "", }, "allow_signal_out_of_range": { Type: schema.TypeString, Optional: true, Description: "", DiffSuppressFunc: TimeEqual, }, "area_prefix": { Type: schema.TypeString, Optional: true, Description: "Rule matches if area value of AP (a proprietary extension) begins with specified value.area " + "value is a proprietary extension.", }, KeyComment: PropCommentRw, "connect": { Type: schema.TypeBool, Optional: true, Description: "Available options: yes - Connect to access point that matches this rule. no - Do not connect " + "to any access point that matches this rule.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyDisabled: PropDisabledRw, KeyInterface: PropInterfaceRw, "interworking": { Type: schema.TypeString, Optional: true, Description: "", ValidateFunc: ValidationAutoYesNo, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "iw_asra": { Type: schema.TypeString, Optional: true, Description: "Additional Steps Required for Access. Set to yes, if a user should take additional steps " + "to access the internet, like the walled garden.", ValidateFunc: ValidationAutoYesNo, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "iw_authentication_types": { Type: schema.TypeString, Optional: true, Description: "This property is only effective when `asra` is set to `yes`. Value of `url` is optional and " + "not needed if `dns-redirection` or `online-enrollment` is selected. To set the value of `url` to empty " + "string use double quotes. For example: `authentication-types=online-enrollment:\"\"`", }, "iw_connection_capabilities": { Type: schema.TypeString, Optional: true, Description: "This option allows to provide information about the allowed IP protocols and ports. This " + "information can be provided in ANQP response. The first number represents the IP protocol number, " + "the second number represents a port number.\n * closed - set if protocol and port combination is not " + "allowed;\n * open - set if protocol and port combination is allowed;\n * unknown - set if protocol " + "and port combination is either open or closed.\n\nExample: `connection-capabilities=6:80:open,17:5060:closed`" + "Setting such a value on an Access Point informs the Wireless client, which is connecting to the " + "Access Point, that HTTP (6 - TCP, 80 - HTTP) is allowed and VoIP (17 - UDP; 5060 - VoIP) is not " + "allowed. This property does not restrict or allow usage of these protocols and ports, it only gives " + "information to station device which is connecting to Access Point.", }, "iw_esr": { Type: schema.TypeString, Optional: true, Description: "Emergency services reachable (ESR). Set to yes in order to indicate that emergency " + "services are reachable through the access point.", ValidateFunc: ValidationAutoYesNo, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "iw_hessid": { Type: schema.TypeString, Optional: true, Description: "Homogenous extended service set identifier (HESSID). Devices that provide access to same " + "external networks are in one homogenous extended service set. This service set can be identified by " + "HESSID that is the same on all access points in this set. 6-byte value of HESSID is represented as MAC " + "address. It should be globally unique, therefore it is advised to use one of the MAC address of access " + "point in the service set.", ValidateFunc: validation.IsMACAddress, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "iw_hotspot20": { Type: schema.TypeString, Optional: true, Description: "Indicate Hotspot 2.0 capability of the Access Point.", ValidateFunc: ValidationAutoYesNo, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "iw_hotspot20_dgaf": { Type: schema.TypeString, Optional: true, Description: "Downstream Group-Addressed Forwarding (DGAF). Sets value of DGAF bit to indicate whether " + "multicast and broadcast frames to clients are disabled or enabled.\n * yes - multicast and broadcast " + "frames to clients are enabled;\n * no - multicast and broadcast frames to clients are disabled.\n" + "To disable multicast and broadcast frames set `multicast-helper=full`.", ValidateFunc: ValidationAutoYesNo, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "iw_internet": { Type: schema.TypeString, Optional: true, Description: "Whether the internet is available through this connection or not. This information is " + "included in the Interworking element.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "iw_ipv4_availability": { Type: schema.TypeString, Optional: true, Description: "Information about what IPv4 address and access are available." + "\n * not-available - Address type not available;" + "\n * public - public IPv4 address available;" + "\n * port-restricted - port-restricted IPv4 address available;" + "\n * single-nated - single NATed private IPv4 address available;" + "\n * double-nated - double NATed private IPv4 address available;" + "\n * port-restricted-single-nated -port-restricted IPv4 address and single NATed IPv4 address available;" + "\n * port-restricted-double-nated - port-restricted IPv4 address and double NATed IPv4 address available;" + "\n * unknown - availability of the address type is not known.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "iw_ipv6_availability": { Type: schema.TypeString, Optional: true, Description: "Information about what IPv6 address and access are available." + "\n * not-available - Address type not available;" + "\n * available - address type available;" + "\n * unknown - availability of the address type is not known.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "iw_network_type": { Type: schema.TypeString, Optional: true, Description: "Information about network access type." + "\n * emergency-only - a network dedicated and limited to accessing emergency services;" + "\n * personal-device - a network of personal devices. An example of this type of network is a camera " + "that is attached to a printer, thereby forming a network for the purpose of printing pictures;" + "\n * private - network for users with user accounts. Usually used in enterprises for employees, not guests;" + "\n * private-with-guest - same as private, but guest accounts are available;" + "\n * public-chargeable - a network that is available to anyone willing to pay. For example, a " + "subscription to Hotspot 2.0 service or in-room internet access in a hotel;" + "\n * public-free - network is available to anyone without any fee. For example, municipal network in " + "city or airport Hotspot;" + "\n * test - network used for testing and experimental uses. Not used in production;" + "\n * wildcard - is used on Wireless clients. Sending probe request with a wildcard as network type " + "value will make all Interworking Access Points respond despite their actual network-type setting." + "\n\nA client sends a probe request frame with network-type set to value it is interested in. It will " + "receive replies only from access points with the same value (except the case of wildcard).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "iw_realms": { Type: schema.TypeString, Optional: true, Description: "Information about supported realms and the corresponding EAP method. " + "`realms=example.com:eap-tls,foo.ba:not-specified`", }, "iw_roaming_ois": { Type: schema.TypeString, Optional: true, Description: "Organization identifier (OI) usually are 24-bit is unique identifiers like organizationally " + "unique identifier (OUI) or company identifier (CID). In some cases, OI is longer for example OUI-36." + "A subscription service provider (SSP) can be specified by its OI. roaming-ois property can contain " + "zero or more SSPs OIs whose networks are accessible via this AP. Length of OI should be specified " + "before OI itself. For example, to set E4-8D-8C and 6C-3B-6B: `roaming-ois=03E48D8C036C3B6B`", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "iw_uesa": { Type: schema.TypeString, Optional: true, Description: "Unauthenticated emergency service accessible (UESA)." + "\n * no - indicates that no unauthenticated emergency services are reachable through this Access Point;" + "\n * yes - indicates that higher layer unauthenticated emergency services are reachable through this Access Point.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "iw_venue": { Type: schema.TypeString, Optional: true, Description: "Specify the venue in which the Access Point is located. Choose the value from available " + "ones. Some examples:\n ```\n" + " venue=business-bank\n" + " venue=mercantile-shopping-mall\n" + " venue=educational-university-or-college\n ```", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyMacAddress: PropMacAddressRw("Rule matches only AP with the specified MAC address.", false), "security_profile": { Type: schema.TypeString, Optional: true, Description: "Name of security profile that is used when connecting to matching access points, If value " + "of this property is none, then security profile specified in the interface configuration will be used. " + "In station mode, rule will match only access points that can support specified security profile. Value " + "none will match access point that supports security profile that is specified in the interface configuration. " + "In access point mode value of this property will not be used to match remote devices.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "signal_range": { Type: schema.TypeString, Optional: true, Description: "Rule matches if signal strength of the access point is within the range. If station establishes " + "connection to access point that is matched by this rule, it will disconnect from that access point when " + "signal strength goes out of the specified range.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "ssid": { Type: schema.TypeString, Optional: true, Description: "Rule matches access points that have this SSID. Empty value matches any SSID. This property " + "has effect only when station mode interface ssid is empty, or when access point mode interface has wds-ignore-ssid=yes.", }, "wireless_protocol": { Type: schema.TypeString, Optional: true, Description: "", ValidateFunc: validation.StringInSlice([]string{"802.11", "any", "nstreme", "tdma"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_wireless_security_profiles.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*0", "authentication-types": "wpa2-psk", "comment": "defconf", "default": "true", "disable-pmkid": "true", "eap-methods": "passthrough", "group-ciphers": "aes-ccm", "group-key-update": "5m", "interim-update": "0s", "management-protection": "disabled", "management-protection-key": "", "mode": "dynamic-keys", "mschapv2-password": "", "mschapv2-username": "", "name": "default", "radius-called-format": "mac:ssid", "radius-eap-accounting": "false", "radius-mac-accounting": "false", "radius-mac-authentication": "false", "radius-mac-caching": "disabled", "radius-mac-format": "XX:XX:XX:XX:XX:XX", "radius-mac-mode": "as-username", "static-algo-0": "none", "static-algo-1": "none", "static-algo-2": "none", "static-algo-3": "none", "static-key-0": "", "static-key-1": "", "static-key-2": "", "static-key-3": "", "static-sta-private-algo": "none", "static-sta-private-key": "", "static-transmit-key": "key-0", "supplicant-identity": "MikroTik", "tls-certificate": "none", "tls-mode": "no-certificates", "unicast-ciphers": "aes-ccm", "wpa-pre-shared-key": "", "wpa2-pre-shared-key": "XXX" } */ // https://help.mikrotik.com/docs/display/ROS/Wireless+Interface#WirelessInterface-SecurityProfiles func ResourceInterfaceWirelessSecurityProfiles() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/wireless/security-profiles"), MetaId: PropId(Id), "authentication_types": { Type: schema.TypeSet, Optional: true, Description: "Set of supported authentication types, multiple values can be selected. Access Point will " + "advertise supported authentication types, and client will connect to Access Point only if it supports " + "any of the advertised authentication types.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"wpa-psk", "wpa2-psk", "wpa-eap", "wpa2-eap"}, false), }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyComment: PropCommentRw, KeyDefault: PropDefaultRo, "disable_pmkid": { Type: schema.TypeBool, Optional: true, Description: "Whether to include `PMKID` into the `EAPOL` frame sent out by the Access Point. Disabling PMKID " + "can cause compatibility issues with devices that use the PMKID to connect to an Access Point. `yes` - removes " + "PMKID from EAPOL frames (improves security, reduces compatibility). `no` - includes PMKID into EAPOL frames " + "(reduces security, improves compatibility).This property only has effect on Access Points.", }, "eap_methods": { Type: schema.TypeString, Optional: true, Description: "Allowed types of authentication methods, multiple values can be selected. This property only " + "has effect on Access Points. `eap-tls` - Use built-in EAP TLS authentication. Both client and server certificates " + "are supported. See description of tls-mode and tls-certificate properties. `eap-ttls-mschapv2` - Use EAP-TTLS " + "with MS-CHAPv2 authentication. `passthrough` - Access Point will relay authentication process to the RADIUS " + "server. `peap` - Use Protected EAP authentication.", ValidateFunc: validation.StringInSlice([]string{"eap-tls", "eap-ttls-mschapv2", "passthrough", "peap"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "group_ciphers": { Type: schema.TypeString, Optional: true, Description: "Access Point advertises one of these ciphers, multiple values can be selected. Access Point " + "uses it to encrypt all broadcast and multicast frames. Client attempts connection only to Access Points " + "that use one of the specified group ciphers. `tkip` - Temporal Key Integrity Protocol - encryption protocol, " + "compatible with legacy WEP equipment, but enhanced to correct some of the WEP flaws. `aes-ccm` - more secure " + "WPA encryption protocol, based on the reliable AES (Advanced Encryption Standard). Networks free of " + "WEP legacy should use only this cipher.", ValidateFunc: validation.StringInSlice([]string{"tkip", "aes-ccm"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "group_key_update": { Type: schema.TypeString, Optional: true, Description: "Controls how often Access Point updates the group key. This key is used to encrypt all broadcast " + "and multicast frames. property only has effect for Access Points.", DiffSuppressFunc: TimeEqual, }, "interim_update": { Type: schema.TypeString, Optional: true, Description: "When RADIUS accounting is used, Access Point periodically sends accounting information updates " + "to the RADIUS server. This property specifies default update interval that can be overridden by the " + "RADIUS server using Acct-Interim-Interval attribute.", DiffSuppressFunc: TimeEqual, }, "management_protection": { Type: schema.TypeString, Optional: true, Description: "Management frame protection. Used for: Deauthentication attack prevention, MAC address cloning " + "issue. Possible values are: `disabled` - management protection is disabled (default), `allowed` - use " + "management protection if supported by remote party (for AP - allow both, non-management protection " + "and management protection clients, for client - connect both to APs with and without management " + "protection), `required` - establish association only with remote devices that support management " + "protection (for AP - accept only clients that support management protection, for client - connect " + "only to APs that support management protection).", ValidateFunc: validation.StringInSlice([]string{"disabled", "allowed", "required"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "management_protection_key": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "Management protection shared secret. When interface is in AP mode, default management " + "protection key (configured in security-profile) can be overridden by key specified in access-list or " + "RADIUS attribute.", }, "mode": { Type: schema.TypeString, Optional: true, Description: "Encryption mode for the security profile. `none` - Encryption is not used. Encrypted frames are " + "not accepted. `static-keys-required` - WEP mode. Do not accept and do not send unencrypted frames. Station " + "in static-keys-required mode will not connect to an Access Point in static-keys-optional mode. `static-keys-optional` - " + "WEP mode. Support encryption and decryption, but allow also to receive and send unencrypted frames. " + "Device will send unencrypted frames if encryption algorithm is specified as none. Station in static-keys-optional mode " + "will not connect to an Access Point in `static-keys-required` mode. See also: static-sta-private-algo, " + "static-transmit-key. `dynamic-keys` - WPA mode.", ValidateFunc: validation.StringInSlice([]string{"none", "static-keys-optional", "static-keys-required", "dynamic-keys"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "mschapv2_password": { Type: schema.TypeString, Optional: true, Description: "Password to use for authentication when `eap-ttls-mschapv2` or `peap` authentication method is " + "being used. This property only has effect on Stations.", }, "mschapv2_username": { Type: schema.TypeString, Optional: true, Description: "Username to use for authentication when `eap-ttls-mschapv2` or `peap` authentication method is " + "being used. This property only has effect on Stations.", }, KeyName: PropName("Name of the security profile."), "radius_called_format": { Type: schema.TypeString, Optional: true, Description: "mac | mac:ssid | ssid", ValidateFunc: validation.StringInSlice([]string{"mac", "mac:ssid", "ssid"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "radius_eap_accounting": { Type: schema.TypeBool, Optional: true, }, "radius_mac_accounting": { Type: schema.TypeBool, Optional: true, }, "radius_mac_authentication": { Type: schema.TypeBool, Optional: true, Description: "This property affects the way how Access Point processes clients that are not found in the Access " + "List.no - allow or reject client authentication based on the value of default-authentication property " + "of the Wireless interface.yes - Query RADIUS server using MAC address of client as user name. With this " + "setting the value of default-authentication has no effect.", }, "radius_mac_caching": { Type: schema.TypeString, Optional: true, Description: "If this value is set to time interval, the Access Point will cache RADIUS MAC authentication " + "responses for specified time, and will not contact RADIUS server if matching cache entry already exists. " + "Value disabled will disable cache, Access Point will always contact RADIUS server.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "radius_mac_format": { Type: schema.TypeString, Optional: true, Description: "Controls how MAC address of the client is encoded by Access Point in the User-Name attribute " + "of the MAC authentication and MAC accounting RADIUS requests.", ValidateFunc: validation.StringInSlice([]string{"XX:XX:XX:XX:XX:XX", "XXXX:XXXX:XXXX", "XXXXXX:XXXXXX", "XX-XX-XX-XX-XX-XX", "XXXXXX-XXXXXX", "XXXXXXXXXXXX", "XX XX XX XX XX XX"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "radius_mac_mode": { Type: schema.TypeString, Optional: true, Description: "By default Access Point uses an empty password, when sending Access-Request during MAC authentication. " + "When this property is set to `as-username-and-password`, Access Point will use the same value for User-Password " + "attribute as for the User-Name attribute.", ValidateFunc: validation.StringInSlice([]string{"as-username", "as-username-and-password"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "static_algo_0": { Type: schema.TypeString, Optional: true, Description: "Encryption algorithm to use with the corresponding key.", ValidateFunc: validation.StringInSlice([]string{"none", "40bit-wep", "104bit-wep", "tkip", "aes-ccm"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "static_algo_1": { Type: schema.TypeString, Optional: true, Description: "Encryption algorithm to use with the corresponding key.", ValidateFunc: validation.StringInSlice([]string{"none", "40bit-wep", "104bit-wep", "tkip", "aes-ccm"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "static_algo_2": { Type: schema.TypeString, Optional: true, Description: "Encryption algorithm to use with the corresponding key.", ValidateFunc: validation.StringInSlice([]string{"none", "40bit-wep", "104bit-wep", "tkip", "aes-ccm"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "static_algo_3": { Type: schema.TypeString, Optional: true, Description: "Encryption algorithm to use with the corresponding key.", ValidateFunc: validation.StringInSlice([]string{"none", "40bit-wep", "104bit-wep", "tkip", "aes-ccm"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "static_key_0": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "Hexadecimal representation of the key. Length of key must be appropriate for selected algorithm. " + "See the Statically configured WEP keys section.", }, "static_key_1": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "Hexadecimal representation of the key. Length of key must be appropriate for selected algorithm. " + "See the Statically configured WEP keys section.", }, "static_key_2": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "Hexadecimal representation of the key. Length of key must be appropriate for selected algorithm. " + "See the Statically configured WEP keys section.", }, "static_key_3": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "Hexadecimal representation of the key. Length of key must be appropriate for selected algorithm. " + "See the Statically configured WEP keys section.", }, "static_sta_private_algo": { Type: schema.TypeString, Optional: true, Description: "Encryption algorithm to use with station private key. Value none disables use of the private " + "key. This property is only used on Stations. Access Point has to get corresponding value either from private-algo property, " + "or from Mikrotik-Wireless-Enc-Algo attribute. Station private key replaces key 0 for unicast frames. " + "Station will not use private key to decrypt broadcast frames.", ValidateFunc: validation.StringInSlice([]string{"none", "40bit-wep", "104bit-wep", "tkip", "aes-ccm"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "static_sta_private_key": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "Length of key must be appropriate for selected algorithm, see the Statically configured WEP " + "keys section. This property is used only on Stations. Access Point uses corresponding key either from " + "private-key property, or from Mikrotik-Wireless-Enc-Key attribute.", }, "static_transmit_key": { Type: schema.TypeString, Optional: true, Description: "Access Point will use the specified key to encrypt frames for clients that do not use private " + "key. Access Point will also use this key to encrypt broadcast and multicast frames. Client will use " + "the specified key to encrypt frames if static-sta-private-algo is set to none. If corresponding static-algo-N property " + "has value set to none, then frame will be sent unencrypted (when mode is set to static-keys-optional) " + "or will not be sent at all (when mode is set to static-keys-required).", ValidateFunc: validation.StringInSlice([]string{"key-0", "key-1", "key-2", "key-3"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "supplicant_identity": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "EAP identity that is sent by client at the beginning of EAP authentication. This value is " + "used as a value for User-Name attribute in RADIUS messages sent by RADIUS EAP accounting and RADIUS " + "EAP pass-through authentication.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "tls_certificate": { Type: schema.TypeString, Optional: true, Description: "Access Point always needs a certificate when configured when tls-mode is set to verify-certificate, " + "or is set to dont-verify-certificate. Client needs a certificate only if Access Point is configured " + "with tls-mode set to verify-certificate. In this case client needs a valid certificate that is signed " + "by a CA known to the Access Point. This property only has effect when tls-mode is not set to " + "no-certificates and eap-methods contains eap-tls.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "tls_mode": { Type: schema.TypeString, Optional: true, Description: "This property has effect only when eap-methods contains eap-tls. `verify-certificate` - Require " + "remote device to have valid certificate. Check that it is signed by known certificate authority. No " + "additional identity verification is done. Certificate may include information about time period during " + "which it is valid. If router has incorrect time and date, it may reject valid certificate because router's " + "clock is outside that period. See also the Certificates configuration. `dont-verify-certificate` - Do not " + "check certificate of the remote device. Access Point will not require client to provide certificate. " + "`no-certificates` - Do not use certificates. TLS session is established using 2048 bit anonymous " + "Diffie-Hellman key exchange. `verify-certificate-with-crl` - Same as verify-certificate but also " + "checks if the certificate is valid by checking the Certificate Revocation List.", ValidateFunc: validation.StringInSlice([]string{"verify-certificate", "dont-verify-certificate", "no-certificates", "verify-certificate-with-crl"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "unicast_ciphers": { Type: schema.TypeString, Optional: true, Description: "Access Point advertises that it supports specified ciphers, multiple values can be selected. " + "Client attempts connection only to Access Points that supports at least one of the specified ciphers. " + "One of the ciphers will be used to encrypt unicast frames that are sent between Access Point and Station.", ValidateFunc: validation.StringInSlice([]string{"tkip", "aes-ccm"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "wpa_pre_shared_key": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "`WPA` pre-shared key mode requires all devices in a BSS to have common secret key. Value of " + "this key can be an arbitrary text. Commonly referred to as the network password for WPA mode. property " + "only has effect when wpa-psk is added to authentication-types.", ValidateFunc: validation.StringLenBetween(8, 64), }, "wpa2_pre_shared_key": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "`WPA2` pre-shared key mode requires all devices in a BSS to have common secret key. Value of " + "this key can be an arbitrary text. Commonly referred to as the network password for WPA2 mode. property " + "only has effect when wpa2-psk is added to authentication-types.", ValidateFunc: validation.StringLenBetween(8, 64), }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_interface_wireless_security_profiles_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterfaceWirelessSecurityProfiles = "routeros_interface_wireless_security_profiles.test" func TestAccInterfaceWirelessSecurityProfilesTest_basic(t *testing.T) { if !testCheckMaxVersion(t, testCapsManChannelMaxVersion) { t.Logf("Test skipped, the maximum supported version is %v", testCapsManChannelMaxVersion) return } // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/interface/wireless/security-profiles", "routeros_interface_wireless_security_profiles"), Steps: []resource.TestStep{ { Config: testAccInterfaceWirelessSecurityProfilesConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceWirelessSecurityProfiles), resource.TestCheckResourceAttr(testInterfaceWirelessSecurityProfiles, "name", "test-profile"), resource.TestCheckResourceAttr(testInterfaceWirelessSecurityProfiles, "mode", "dynamic-keys"), resource.TestCheckResourceAttr(testInterfaceWirelessSecurityProfiles, "wpa_pre_shared_key", "wpa_psk_key"), resource.TestCheckResourceAttr(testInterfaceWirelessSecurityProfiles, "wpa2_pre_shared_key", "wpa2_psk_key"), ), }, }, }) }) } } func testAccInterfaceWirelessSecurityProfilesConfig() string { return fmt.Sprintf(`%v resource "routeros_interface_wireless_security_profiles" "test" { name = "test-profile" mode = "dynamic-keys" authentication_types = ["wpa-psk", "wpa2-psk"] wpa_pre_shared_key = "wpa_psk_key" wpa2_pre_shared_key = "wpa2_psk_key" } `, providerConfig) } ================================================ FILE: routeros/resource_interface_wireless_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterfaceWireless = "routeros_interface_wireless.test" func TestAccInterfaceWirelessTest_basic(t *testing.T) { t.Logf("A device with WiFi interface is required for the test") return for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/interface/wireless", "routeros_interface_wireless"), Steps: []resource.TestStep{ { Config: testAccInterfaceWirelessConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceWireless), resource.TestCheckResourceAttr(testInterfaceWireless, "", ""), ), }, }, }) }) } } func testAccInterfaceWirelessConfig() string { return fmt.Sprintf(`%v resource "routeros_interface_wireless_security_profiles" "test" { name = "test-profile" mode = "dynamic-keys" authentication_types = ["wpa-psk", "wpa2-psk"] wpa_pre_shared_key = "wpa_psk_key" wpa2_pre_shared_key = "wpa2_psk_key" } resource "routeros_interface_wireless" "test" { depends_on = [resource.routeros_interface_wireless_security_profiles.test] security_profile=resource.routeros_interface_wireless_security_profiles.test.name mode="ap-bridge" master_interface="wlan1" name="wlan-guest" ssid="guests" basic_rates_ag = ["6Mbps", "9Mbps"] } `, providerConfig) } ================================================ FILE: routeros/resource_ip_address.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*2", "actual-interface": "vlan2", "address": "10.0.0.1/24", "disabled": "false", "dynamic": "false", "interface": "vlan15", "invalid": "false", "network": "10.0.0.0" }, */ // ResourceIPAddress https://wiki.mikrotik.com/wiki/Manual:IP/Address func ResourceIPAddress() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/address"), MetaId: PropId(Id), "address": { Type: schema.TypeString, Required: true, Description: "IP address.", ValidateFunc: ValidationIpAddress, }, "actual_interface": { Type: schema.TypeString, Computed: true, Description: "Name of the actual interface the logical one is bound to.", }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, KeyInterface: PropInterfaceRw, KeyInvalid: PropInvalidRo, "network": { Type: schema.TypeString, Optional: true, Computed: true, Description: "IP address for the network. For point-to-point links it should be the address of the " + "remote end. Starting from v5RC6 this parameter is configurable only for addresses with /32 netmask " + "(point to point links)", ValidateFunc: validation.IsIPAddress, }, "slave": { Type: schema.TypeBool, Computed: true, Description: "Whether address belongs to an interface which is a slave port to some other master interface", }, KeyVrf: PropVrfRw, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_address_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterfaceIPAddressAddress = "routeros_ip_address.test_ip_address" func TestAccInterfaceIPAddressTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/address", "routeros_ip_address"), Steps: []resource.TestStep{ { Config: testAccInterfaceIPAddressConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceIPAddressAddress), resource.TestCheckResourceAttr(testInterfaceIPAddressAddress, "address", "172.16.255.254/32"), ), }, }, }) }) } } func testAccInterfaceIPAddressConfig() string { return providerConfig + ` resource "routeros_ip_address" "test_ip_address" { interface = "bridge" address = "172.16.255.254/32" } ` } ================================================ FILE: routeros/resource_ip_cloud.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { "ddns-enabled": "true", "ddns-update-interval": "1m", "dns-name": "ad8a0be701ea.sn.mynetname.net", "public-address": "31.173.86.120", "status": "updated", "update-time": "true", "warning": "Router is behind a NAT. Remote connection might not work." } */ // https://wiki.mikrotik.com/wiki/Manual:IP/Cloud // https://help.mikrotik.com/docs/display/ROS/Cloud func ResourceIpCloud() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/cloud"), MetaId: PropId(Id), // https://help.mikrotik.com/docs/spaces/ROS/pages/197984280/Back+To+Home MetaSkipFields: PropSkipFields("vpn_dns_name", "vpn_interface", "vpn_peer_private_key", "vpn_peer_public_key", "vpn_port", "vpn_private_key", "vpn_public_key", "vpn_relay_addressess", "vpn_relay_addressess_ipv6", "vpn_relay_codes", "vpn_relay_ipv4_status", "vpn_relay_ipv6_status", "vpn_relay_regions", "vpn_relay_rtts", "vpn_status", "vpn_wireguard_client_config", "vpn_wireguard_client_config_qrcode"), // https://help.mikrotik.com/docs/display/ROS/Back+To+Home "back_to_home_vpn": { Type: schema.TypeString, Optional: true, Description: "Enables or revokes and disables the Back to Home service. ddns-enabled has to be set to " + "yes, for BTH to function.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "ddns_enabled": { Type: schema.TypeString, Optional: true, Description: "If set to yes, then the device will send an encrypted message to the MikroTik's Cloud " + "server. The server will then decrypt the message and verify that the sender is an " + "authentic MikroTik device. If all is OK, then the MikroTik's Cloud server will create a " + "DDNS record for this device and send a response to the device. Every minute the IP/Cloud " + "service on the router will check if WAN IP address matches the one sent to MikroTik's " + "Cloud server and will send encrypted update to cloud server if IP address changes.", }, "ddns_update_interval": { Type: schema.TypeString, Optional: true, Default: "none", Description: "If set DDNS will attempt to connect IP Cloud servers at the set interval. If set to none " + "it will continue to internally check IP address update and connect to IP Cloud servers " + "as needed. Useful if IP address used is not on the router itself and thus, cannot be " + "checked as a value internal to the router.", DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { if old == new { return true } if old == "none" || new == "none" { return false } return TimeEqual(k, old, new, d) }, }, "dns_name": { Type: schema.TypeString, Computed: true, Description: "Shows DNS name assigned to the rdevice. Name consists of 12 character serial number " + "appended by .sn.mynetname.net. This field is visible only after at least one " + "ddns-request is successfully completed.", }, "public_address": { Type: schema.TypeString, Computed: true, Description: "Shows device's IPv4 address that was sent to cloud server. This field is visible only " + "after at least one IP Cloud request was successfully completed.", }, "public_address_ipv6": { Type: schema.TypeString, Computed: true, Description: "Shows device's IPv6 address that was sent to cloud server. This field is visible only " + "after at least one IP Cloud request was successfully completed.", }, "status": { Type: schema.TypeString, Computed: true, Description: "Contains text string that describes current dns-service state. The messages are self " + "explanatory updating... updated Error: no Internet connection Error: request timed out " + "Error: REJECTED. Contact MikroTik support Error: internal error - should not happen. One " + "possible cause is if router runs out of memory.", }, "update_time": { Type: schema.TypeString, Optional: true, Description: "If set to yes then router clock will be set to time, provided by cloud server IF there " + "is no NTP or SNTP client enabled. If set to no, then IP/Cloud service will never update " + "the device's clock. If update-time is set to yes, Clock will be updated even when " + "ddns-enabled is set to no.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "vpn_prefer_relay_code": { Type: schema.TypeString, Optional: true, Description: "You can enter relay code that will be preferred for BTH connection, if not set, relay with " + "smallest RTT will be chosen.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "warning": { Type: schema.TypeString, Computed: true, Description: "Shows a warning message if IP address sent by the device differs from the IP address in " + "UDP packet header as visible by the MikroTik's Cloud server. Typically this happens if " + "the device is behind NAT. Example: 'DDNS server received request from IP 123.123.123.123 " + "but your local IP was 192.168.88.23; DDNS service might not work'", }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_cloud_advanced.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { "use-local-address":"false" } */ // https://help.mikrotik.com/docs/display/ROS/Cloud#Cloud-Advanced func ResourceIpCloudAdvanced() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/cloud/advanced"), MetaId: PropId(Id), "use_local_address": { Type: schema.TypeBool, Optional: true, Default: false, Description: "An option whether to assign an internal router address to the dynamic DNS name.", }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_cloud_advanced_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpCloudAdvancedAddress = "routeros_ip_cloud_advanced.test" func TestAccIpCloudAdvancedTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccIpCloudAdvancedConfig(0), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpCloudAdvancedAddress), resource.TestCheckResourceAttr(testIpCloudAdvancedAddress, "use_local_address", "true"), ), }, { Config: testAccIpCloudAdvancedConfig(1), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpCloudAdvancedAddress), resource.TestCheckResourceAttr(testIpCloudAdvancedAddress, "use_local_address", "false"), ), }, }, }) }) } } func testAccIpCloudAdvancedConfig(n int) string { var conf = []string{ ` resource "routeros_ip_cloud_advanced" "test" { use_local_address = true }`, ` resource "routeros_ip_cloud_advanced" "test" { use_local_address = false }`, } return providerConfig + conf[n] } ================================================ FILE: routeros/resource_ip_cloud_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpCloudAddress = "routeros_ip_cloud.test" func TestAccIpCloudTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccIpCloudConfig(0), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpCloudAddress), resource.TestCheckResourceAttr(testIpCloudAddress, "ddns_update_interval", "none"), ), }, { Config: testAccIpCloudConfig(1), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpCloudAddress), resource.TestCheckResourceAttr(testIpCloudAddress, "ddns_update_interval", "15m"), ), }, { Config: testAccIpCloudConfig(2), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpCloudAddress), resource.TestCheckResourceAttr(testIpCloudAddress, "ddns_update_interval", "10m"), ), }, { Config: testAccIpCloudConfig(3), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpCloudAddress), resource.TestCheckResourceAttr(testIpCloudAddress, "ddns_update_interval", "none"), ), }, }, }) }) } } func testAccIpCloudConfig(n int) string { var conf = []string{ ` resource "routeros_ip_cloud" "test" { ddns_enabled = true ddns_update_interval = "none" }`, ` resource "routeros_ip_cloud" "test" { ddns_enabled = true ddns_update_interval = "15m" }`, ` resource "routeros_ip_cloud" "test" { ddns_enabled = true ddns_update_interval = "600" }`, ` resource "routeros_ip_cloud" "test" { ddns_enabled = true }`, } return providerConfig + conf[n] } ================================================ FILE: routeros/resource_ip_dhcp_client.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) // ResourceDhcpClient https://help.mikrotik.com/docs/display/ROS/DHCP#DHCP-DHCPClient func ResourceDhcpClient() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/dhcp-client"), MetaId: PropId(Id), "add_default_route": { Type: schema.TypeString, Optional: true, Computed: true, Description: "Whether to install default route in routing table received from DHCP server.", ValidateFunc: validation.StringInSlice([]string{"yes", "no", "special-classless"}, false), }, "address": { Type: schema.TypeString, Computed: true, Description: "IP address and netmask, which is assigned to DHCP Client from the Server.", }, "allow_reconfigure": { Type: schema.TypeBool, Optional: true, Description: "", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "check_gateway": { Type: schema.TypeString, Optional: true, Description: "Method on how to check gateway reachability.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyComment: PropCommentRw, "default_route_distance": { Type: schema.TypeInt, Optional: true, Default: 1, Description: "Distance of default route. Applicable if add-default-route is set to yes.", ValidateFunc: validation.IntBetween(0, 255), // Default route distance returns as empty when the dhcp-client is searching. // This produces inconsistent results, for this case, we will suppress changes. DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { if old == new || new == "" { return true } return false }, }, "default_route_tables": { Type: schema.TypeSet, Optional: true, Description: "Default route tables.", Elem: &schema.Schema{ Type: schema.TypeString, }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "dhcp_options": { Type: schema.TypeString, Optional: true, Default: "hostname,clientid", Description: "Options that are sent to the DHCP server.", }, "dhcp_server": { Type: schema.TypeString, Computed: true, Description: "The IP address of the DHCP server.", }, KeyDisabled: PropDisabledRw, "dscp": { Type: schema.TypeInt, Optional: true, Description: "Sets the DSCP (Differentiated Services Code Point) value for outgoing DHCP client packets. " + "This value is part of the IP header and is used to indicate the desired Quality of Service (QoS) " + "level for network traffic.", ValidateFunc: validation.IntBetween(0, 63), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyDynamic: PropDynamicRo, "expires_after": { Type: schema.TypeString, Computed: true, Description: "A time when the lease expires (specified by the DHCP server).", }, "gateway": { Type: schema.TypeString, Computed: true, Description: "The IP address of the gateway which is assigned by DHCP server.", }, KeyInterface: PropInterfaceRw, KeyInvalid: PropInvalidRo, "primary_dns": { Type: schema.TypeString, Computed: true, Description: "The IP address of the first DNS resolver, that was assigned by the DHCP server.", }, "primary_ntp": { Type: schema.TypeString, Computed: true, Description: "The IP address of the primary NTP server, assigned by the DHCP server.", }, "script": { Type: schema.TypeString, Optional: true, Description: "A script.", }, "secondary_dns": { Type: schema.TypeString, Computed: true, Description: "The IP address of the second DNS resolver, assigned by the DHCP server.", }, "secondary_ntp": { Type: schema.TypeString, Computed: true, Description: "The IP address of the secondary NTP server, assigned by the DHCP server.", }, "status": { Type: schema.TypeString, Computed: true, }, "use_broadcast": { Type: schema.TypeString, Optional: true, Description: "Whether to set broadcast bit in DHCPDISCOVER and DHCPREQUEST messages." + "\n - `always` - broadcast bit is set always" + "\n - `both` - broadcast bit is set only first 15 seconds" + "\n - `never` - broadcast bit is not set", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "use_peer_dns": { Type: schema.TypeBool, Optional: true, Default: true, Description: "Whether to accept the DNS settings advertised by DHCP Server (will override the settings " + "put in the /ip dns submenu).", }, "use_peer_ntp": { Type: schema.TypeBool, Optional: true, Default: true, Description: "Whether to accept the NTP settings advertised by DHCP Server (will override the settings " + "put in the /system ntp client submenu).", }, "use_reconfigure": { Type: schema.TypeBool, Optional: true, Description: "Allow the server to send Reconfigure messages to clients, prompting them to renew or " + "update their configuration without waiting for their lease to expire.", }, "vlan_priority": { Type: schema.TypeInt, Optional: true, Description: "If the DHCP client is running on a VLAN interface (`/interface/vlan`), you can specify the " + "Priority Code Point (PCP) value. PCP is a 3-bit field in the VLAN header used to mark the priority of " + "packets within a VLAN, allowing traffic to be prioritized accordingly. This setting applies only to " + "VLAN interfaces and affects the priority of outgoing DHCP client packets.", ValidateFunc: validation.IntBetween(0, 7), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_dhcp_client_option.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) // ResourceDhcpClient https://help.mikrotik.com/docs/display/ROS/DHCP#DHCP-DHCPClient func ResourceDhcpClientOption() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/dhcp-client/option"), MetaId: PropId(Id), "name": { Type: schema.TypeString, Required: true, Description: "The name that will be used in dhcp-client.", }, "code": { Type: schema.TypeInt, Required: true, Description: "The dhcp-client option code.", }, "value": { Type: schema.TypeString, Optional: true, Description: "The dhcp-client option", }, "raw_value": { Type: schema.TypeString, Optional: true, Computed: true, Description: "raw_value is computed from value.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_dhcp_client_option_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpDhcpClientOptionAddress = "routeros_ip_dhcp_client_option.test_dhcp" func TestAccIpDhcpClientOptionTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/dhcp-client/option", "routeros_ip_dhcp_client_option"), Steps: []resource.TestStep{ { Config: testAccIpDhcpClientOptionConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpDhcpClientOptionAddress), resource.TestCheckResourceAttr(testIpDhcpClientOptionAddress, "name", "my-dhcp-option"), resource.TestCheckResourceAttr(testIpDhcpClientOptionAddress, "code", "60"), ), }, }, }) }) } } func testAccIpDhcpClientOptionConfig() string { return providerConfig + ` resource "routeros_ip_dhcp_client_option" "test_dhcp" { name = "my-dhcp-option" code = 60 } ` } ================================================ FILE: routeros/resource_ip_dhcp_client_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpDhcpClientAddress = "routeros_ip_dhcp_client.test_dhcp" func TestAccIpDhcpClientTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/dhcp-client", "routeros_ip_dhcp_client"), Steps: []resource.TestStep{ { Config: testAccIpDhcpClientConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpDhcpClientAddress), resource.TestCheckResourceAttr(testIpDhcpClientAddress, "interface", "bridge"), ), }, }, }) }) } } func testAccIpDhcpClientConfig() string { return providerConfig + ` resource "routeros_ip_dhcp_client" "test_dhcp" { interface = "bridge" } ` } ================================================ FILE: routeros/resource_ip_dhcp_relay.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) // ResourceDhcpRelay https://wiki.mikrotik.com/wiki/Manual:IP/DHCP_Relay func ResourceDhcpRelay() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/dhcp-relay"), MetaId: PropId(Id), "add_relay_info": { Type: schema.TypeBool, Optional: true, Description: "Adds DHCP relay agent information if enabled according to RFC 3046. Agent Circuit ID " + "Sub-option contains mac address of an interface, Agent Remote ID Sub-option contains MAC address " + "of the client from which request was received.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "delay_threshold": { Type: schema.TypeString, Optional: true, Description: "If secs field in DHCP packet is smaller than delay-threshold, then this packet is ignored.", DiffSuppressFunc: TimeEqual, }, KeyDisabled: PropDisabledRw, "dhcp_server": { Type: schema.TypeString, Required: true, Description: "List of DHCP servers' IP addresses which should the DHCP requests be forwarded to.", }, "dhcp_server_vrf": PropVrfRw, "interface": { Type: schema.TypeString, Required: true, Description: "Interface name the DHCP relay will be working on.", }, KeyInvalid: PropInvalidRo, "local_address": { Type: schema.TypeString, Optional: true, Description: "The unique IP address of this DHCP relay needed for DHCP server to distinguish relays. " + "If set to 0.0.0.0 - the IP address will be chosen automatically", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyName: PropName("Descriptive name for the relay."), "relay_info_remote_id": { Type: schema.TypeString, Optional: true, Description: "Specified string will be used to construct Option 82 instead of client's MAC address. Option " + "82 consist of: interface from which packets was received + client mac address or relay-info-remote-id", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_dhcp_relay_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpDhcpRelayAddress = "routeros_ip_dhcp_relay.test" func TestAccIpDhcpRelayTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/dhcp-relay", "routeros_ip_dhcp_relay"), Steps: []resource.TestStep{ { Config: testAccIpDhcpRelayConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpDhcpRelayAddress), resource.TestCheckResourceAttr(testIpDhcpRelayAddress, "name", "test relay"), resource.TestCheckResourceAttr(testIpDhcpRelayAddress, "interface", "ether1"), resource.TestCheckResourceAttr(testIpDhcpRelayAddress, "dhcp_server", "0.0.0.1"), ), }, }, }) }) } } func testAccIpDhcpRelayConfig() string { return providerConfig + ` resource "routeros_ip_dhcp_relay" "test" { name = "test relay" interface = "ether1" dhcp_server = "0.0.0.1" } ` } ================================================ FILE: routeros/resource_ip_dhcp_server.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) // ResourceDhcpServer https://help.mikrotik.com/docs/display/ROS/DHCP#DHCP-Leases func ResourceDhcpServer() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/dhcp-server"), MetaId: PropId(Id), "add_arp": { Type: schema.TypeBool, Optional: true, Description: "Whether to add dynamic ARP entry. ", }, "address_pool": { Type: schema.TypeString, Optional: true, Description: "IP pool, from which to take IP addresses for the clients. If set to static-only, then only " + "the clients that have a static lease (added in lease submenu) will be allowed.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "address_lists": { Type: schema.TypeSet, Optional: true, Description: "Address list to which address will be added if lease is bound.", Elem: &schema.Schema{ Type: schema.TypeString, }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "allow_dual_stack_queue": { Type: schema.TypeBool, Optional: true, Description: "Creates a single simple queue entry for both IPv4 and IPv6 addresses, uses the MAC address and " + "DUID for identification. Requires IPv6 DHCP Server to have this option enabled as well to work properly.", }, "always_broadcast": { Type: schema.TypeBool, Optional: true, Description: "Always send replies as broadcasts even if destination IP is known.", }, "authoritative": { Type: schema.TypeString, Optional: true, Description: "Option changes the way how a server responds to DHCP requests.", ValidateFunc: validation.StringInSlice([]string{"after-10sec-delay", "after-2sec-delay", "yes", "no"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "bootp_lease_time": { Type: schema.TypeString, Optional: true, Description: "Accepts two predefined options or time value: * forever - lease never expires " + "* lease-time - use time from lease-time parameter", }, "bootp_support": { Type: schema.TypeString, Optional: true, Description: "Support for BOOTP clients.", ValidateFunc: validation.StringInSlice([]string{"none", "static", "dynamic"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "client_mac_limit": { Type: schema.TypeInt, Optional: true, Description: "Specifies whether to limit specific number of clients per single MAC address.", }, "conflict_detection": { Type: schema.TypeBool, Optional: true, Description: "Allows to disable/enable conflict detection. If option is enabled, then whenever server tries " + "to assign a lease it will send ICMP and ARP messages to detect whether such address in the network " + "already exist. If any of above get reply address is considered already used. Conflict detection must " + "be disabled when any kind of DHCP client limitation per port or per mac is used.", }, KeyComment: PropCommentRw, "delay_threshold": { Type: schema.TypeString, Optional: true, Description: "If secs field in DHCP packet is smaller than delay-threshold, then this packet is ignored. " + "If set to none - there is no threshold (all DHCP packets are processed).", }, "dhcp_option_set": { Type: schema.TypeString, Optional: true, Description: "Use custom set of DHCP options defined in option sets menu.", }, "dynamic_lease_identifiers": { Type: schema.TypeString, Optional: true, Description: "Dynamic lease identifier", }, "support_broadband_tr101": { Type: schema.TypeBool, Optional: true, Description: "Support broadband TR101", }, KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, "insert_queue_before": { Type: schema.TypeString, Optional: true, Description: "Specify where to place dynamic simple queue entries for static DCHP leases with rate-limit parameter set.", }, KeyInterface: PropInterfaceRw, KeyInvalid: PropInvalidRo, "lease_script": { Type: schema.TypeString, Optional: true, Description: "A script that will be executed after a lease is assigned or de-assigned.", }, "lease_time": { Type: schema.TypeString, Optional: true, Description: "The time that a client may use the assigned address. The client will try to renew this " + "address after half of this time and will request a new address after the time limit expires.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyName: PropNameForceNewRw, "parent_queue": { Type: schema.TypeString, Optional: true, Description: "", }, "relay": { Type: schema.TypeString, Optional: true, Description: "The IP address of the relay this DHCP server.", ValidateFunc: ValidationIpAddress, }, "src_address": { Type: schema.TypeString, Optional: true, Description: "The address which the DHCP client must send requests to in order to renew an IP address lease.", ValidateFunc: ValidationIpAddress, }, "use_framed_as_classless": { Type: schema.TypeBool, Optional: true, Description: "Forward RADIUS Framed-Route as a DHCP Classless-Static-Route to DHCP-client.", }, "use_radius": { Type: schema.TypeString, Optional: true, Description: "Whether to use RADIUS server.", ValidateFunc: validation.StringInSlice([]string{"yes", "no", "accounting"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "use_reconfigure": { Type: schema.TypeBool, Optional: true, Description: "Allow the server to send Reconfigure (forcerenew) messages to clients, prompting them to renew " + "configuration without waiting for their lease to expire.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, SchemaVersion: 1, StateUpgraders: []schema.StateUpgrader{ { Type: ResourceDhcpServerV0().CoreConfigSchema().ImpliedType(), Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), Version: 0, }, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_dhcp_server_config.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) // ResourceDhcpServerConfig https://help.mikrotik.com/docs/display/ROS/DHCP#DHCP-StoreConfiguration func ResourceDhcpServerConfig() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/dhcp-server/config"), MetaId: PropId(Id), "accounting": { Type: schema.TypeBool, Optional: true, Default: true, Description: "An option that enables accounting for DHCP leases.", }, "interim_update": { Type: schema.TypeString, Optional: true, Default: "0s", Description: "An option determining whether the DHCP server sends periodic updates to the accounting server during a lease.", DiffSuppressFunc: TimeEqual, }, "radius_password": { Type: schema.TypeString, Optional: true, Default: "empty", Description: "An option to set the password parameter for the RADIUS server. This option is available in RouterOS starting from version 7.0.", ValidateFunc: validation.StringInSlice([]string{"empty", "same-as-user"}, false), }, "store_leases_disk": { Type: schema.TypeString, Optional: true, Default: "5m", Description: "An option of how often the DHCP leases will be stored on disk.", DiffSuppressFunc: TimeEqual, }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_dhcp_server_lease.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) // ResourceDhcpServerLease https://wiki.mikrotik.com/wiki/Manual:IP/DHCP_Server func ResourceDhcpServerLease() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/dhcp-server/lease"), MetaId: PropId(Id), "active_address": { Type: schema.TypeString, Computed: true, Description: "The IP address of the machine currently holding the DHCP lease.", }, "active_client_id": { Type: schema.TypeString, Computed: true, Description: "Actual client-id of the client.", }, "active_hostname": { Type: schema.TypeString, Computed: true, Description: "The hostname of the machine currently holding the DHCP lease.", }, "active_mac_address": { Type: schema.TypeString, Computed: true, Description: "The MAC address of of the machine currently holding the DHCP lease.", }, "address": { Type: schema.TypeString, Required: true, Description: "The IP address of the DHCP lease to be created.", }, "address_lists": { Type: schema.TypeString, Optional: true, Description: "Address list to which address will be added if lease is bound.", }, "active_server": { Type: schema.TypeString, Computed: true, Description: "Actual dhcp server, which serves this client.", }, "agent_circuit_id": { Type: schema.TypeString, Computed: true, Description: "Circuit ID of DHCP relay agent. If each character should be valid ASCII text symbol or else " + "this value is displayed as hex dump.", }, "agent_remote_id": { Type: schema.TypeString, Computed: true, Description: "Remote ID, set by DHCP relay agent.", }, "allow_dual_stack_queue": { Type: schema.TypeBool, Optional: true, Description: "Creates a single simple queue entry for both IPv4 and IPv6 addresses, uses the MAC address " + "and DUID for identification.", }, "always_broadcast": { Type: schema.TypeBool, Optional: true, Description: "Send all replies as broadcasts.", }, "class_id": { Type: schema.TypeString, Computed: true, Description: "Class ID of the client. This option is available in RouterOS starting from version 7.16.", }, "block_access": { Type: schema.TypeBool, Optional: true, Default: false, Description: "Whether to block access for this DHCP client (true|false).", }, "blocked": { Type: schema.TypeBool, Computed: true, Description: "Whether the lease is blocked.", }, // bridge_port ? "client_id": { Type: schema.TypeString, Optional: true, Description: "If specified, must match DHCP 'client identifier' option of the request.", }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "dhcp_option": { Type: schema.TypeString, Optional: true, Description: "Add additional DHCP options.", }, "dhcp_option_set": { Type: schema.TypeString, Optional: true, Description: "Add additional set of DHCP options.", }, "dynamic": { Type: schema.TypeBool, Computed: true, Description: "Whether the dhcp lease is static or dynamic. Dynamic leases are not guaranteed to continue to be assigned to that specific device.", }, "expires_after": { Type: schema.TypeString, Computed: true, Description: "Time until lease expires.", }, "host_name": { Type: schema.TypeString, Computed: true, Description: "The hostname of the device", }, "insert_queue_before": { Type: schema.TypeString, Optional: true, Description: "Specify where to place dynamic simple queue entries for static DCHP leases with " + "rate-limit parameter set.", }, "last_seen": { Type: schema.TypeString, Computed: true, }, "lease_time": { Type: schema.TypeString, Optional: true, Description: "Time that the client may use the address. If set to 0s lease will never expire.", }, "mac_address": { Type: schema.TypeString, Required: true, Description: "The MAC addreess of the DHCP lease to be created.", ValidateFunc: validation.IsMACAddress, DiffSuppressFunc: MacAddressEqual, }, "radius": { Type: schema.TypeString, Computed: true, Description: "Shows if this dynamic lease is authenticated by RADIUS or not.", }, "rate_limit": { Type: schema.TypeString, Optional: true, Description: "Adds a dynamic simple queue to limit IP's bandwidth to a specified rate. " + "Requires the lease to be static.", }, "server": { Type: schema.TypeString, Optional: true, Description: "Server name which serves this client.", }, "status": { Type: schema.TypeString, Computed: true, Description: "Lease status.", }, "src_mac_address": { Type: schema.TypeString, Computed: true, Description: "Source MAC address.", }, "use_src_mac": { Type: schema.TypeBool, Optional: true, Description: "When this option is set server uses source MAC address instead of received CHADDR to " + "assign address.", }, } return &schema.Resource{ Description: "Creates a DHCP lease on the mikrotik device.", CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_dhcp_server_lease_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpDhcpServerLease = "routeros_ip_dhcp_server_lease.test_dhcp_lease" func TestAccIpDhcpServerLeaseTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/dhcp-server/lease", "routeros_i[_dhcp_server_lease"), Steps: []resource.TestStep{ { Config: testAccIpDhcpServerLeaseConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpDhcpServerLease), resource.TestCheckResourceAttr(testIpDhcpServerLease, "address", "192.168.88.33"), resource.TestCheckResourceAttr(testIpDhcpServerLease, "mac_address", "AA:BB:CC:DD:EE:FF"), resource.TestCheckResourceAttr(testIpDhcpServerLease, "block_access", "true"), ), }, }, }) }) } } func testAccIpDhcpServerLeaseConfig() string { return providerConfig + ` resource "routeros_ip_dhcp_server_lease" "test_dhcp_lease" { address = "192.168.88.33" mac_address = "AA:BB:CC:DD:EE:FF" block_access = true } ` } ================================================ FILE: routeros/resource_ip_dhcp_server_network.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) // ResourceDhcpServerNetwork https://help.mikrotik.com/docs/spaces/ROS/pages/24805500/DHCP#DHCP-Network func ResourceDhcpServerNetwork() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/dhcp-server/network"), MetaId: PropId(Id), "address": { Type: schema.TypeString, Required: true, Description: "The network DHCP server(s) will lease addresses from.", }, "boot_file_name": { Type: schema.TypeString, Optional: true, Description: "Boot filename.", }, "caps_manager": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "A list of IP addresses for one or more CAPsMAN system managers. " + "DHCP Option 138 (capwap) will be used.", }, KeyComment: PropCommentRw, "dhcp_option": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "Add additional DHCP options from the option list.", }, "dhcp_option_set": { Type: schema.TypeString, Optional: true, Description: "Add an additional set of DHCP options.", }, "dns_none": { Type: schema.TypeBool, Optional: true, Description: "If set, then DHCP Server will not pass dynamic DNS servers configured on the router to the " + "DHCP clients if no DNS Server in DNS-server is set.", }, "dns_server": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "The DHCP client will use these as the default DNS servers. Two DNS servers " + "can be specified to be used by the DHCP client as primary and secondary DNS servers.", }, "domain": { Type: schema.TypeString, Optional: true, Description: "The DHCP client will use this as the 'DNS domain' setting for the network adapter.", }, KeyDynamic: PropDynamicRo, "gateway": { Type: schema.TypeString, Optional: true, Default: "0.0.0.0", Description: "The default gateway to be used by DHCP Client.", ValidateFunc: validation.IsIPv4Address, }, "netmask": { Type: schema.TypeInt, Optional: true, Default: 0, Description: "The actual network mask is to be used by the DHCP client. If set to '0' - netmask from " + "network address will be used.", ValidateFunc: validation.IntBetween(0, 32), }, "next_server": { Type: schema.TypeString, Optional: true, Description: "The IP address of the next server to use in bootstrap.", ValidateFunc: validation.IsIPv4Address, }, "ntp_none": { Type: schema.TypeBool, Optional: true, Description: "If set, then DHCP Server will not pass NTP servers configured on the router to the DHCP clients.", }, "ntp_server": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.IsIPv4Address, }, Description: "The DHCP client will use these as the default NTP servers. Two NTP servers " + "can be specified to be used by the DHCP client as primary and secondary NTP servers", }, "wins_server": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.IsIPv4Address, }, Description: "The Windows DHCP client will use these as the default WINS servers. Two WINS servers " + "can be specified to be used by the DHCP client as primary and secondary WINS servers", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, SchemaVersion: 1, StateUpgraders: []schema.StateUpgrader{ { Type: ResourceDhcpServerNetworkV0().CoreConfigSchema().ImpliedType(), Upgrade: stateMigrationScalarToList("caps_manager", "dhcp_option", "dns_server", "ntp_server", "wins_server"), Version: 0, }, }, } } ================================================ FILE: routeros/resource_ip_dhcp_server_network_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpDhcpServerNetworkAddress = "routeros_ip_dhcp_server_network.test_dhcp" func TestAccIpDhcpServerNetworkTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/dhcp-server/network", "routeros_ip_dhcp_server_network"), Steps: []resource.TestStep{ { Config: testAccIpDhcpServerNetworkConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpDhcpServerNetworkAddress), resource.TestCheckResourceAttr(testIpDhcpServerNetworkAddress, "address", "192.168.1.0/24"), ), }, }, }) }) } } func testAccIpDhcpServerNetworkConfig() string { return providerConfig + ` resource "routeros_ip_dhcp_server_network" "test_dhcp" { address = "192.168.1.0/24" } ` } ================================================ FILE: routeros/resource_ip_dhcp_server_network_v0.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) // ResourceDhcpServerNetwork https://wiki.mikrotik.com/wiki/Manual:IP/DHCP_Server#Networks func ResourceDhcpServerNetworkV0() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/dhcp-server/network"), MetaId: PropId(Id), "address": { Type: schema.TypeString, Required: true, Description: "The network DHCP server(s) will lease addresses from.", }, "boot_file_name": { Type: schema.TypeString, Optional: true, Description: "Boot filename.", }, "caps_manager": { Type: schema.TypeString, Optional: true, Description: "A comma-separated list of IP addresses for one or more CAPsMAN system managers. " + "DHCP Option 138 (capwap) will be used.", }, KeyComment: PropCommentRw, "dhcp_option": { Type: schema.TypeString, Optional: true, Description: "Add additional DHCP options from the option list.", }, "dhcp_option_set": { Type: schema.TypeString, Optional: true, Description: "Add an additional set of DHCP options.", }, "dns_none": { Type: schema.TypeBool, Optional: true, Description: "If set, then DHCP Server will not pass dynamic DNS servers configured on the router to the " + "DHCP clients if no DNS Server in DNS-server is set.", }, "dns_server": { Type: schema.TypeString, Optional: true, Description: "the DHCP client will use these as the default DNS servers. Two comma-separated DNS servers " + "can be specified to be used by the DHCP client as primary and secondary DNS servers.", }, "domain": { Type: schema.TypeString, Optional: true, Description: "The DHCP client will use this as the 'DNS domain' setting for the network adapter.", }, KeyDynamic: PropDynamicRo, "gateway": { Type: schema.TypeString, Optional: true, Default: "0.0.0.0", Description: "The default gateway to be used by DHCP Client.", ValidateFunc: validation.IsIPv4Address, }, "netmask": { Type: schema.TypeInt, Optional: true, Default: 0, Description: "The actual network mask is to be used by the DHCP client. If set to '0' - netmask from " + "network address will be used.", ValidateFunc: validation.IntBetween(0, 32), }, "next_server": { Type: schema.TypeString, Optional: true, Description: "The IP address of the next server to use in bootstrap.", ValidateFunc: validation.IsIPv4Address, }, "ntp_server": { Type: schema.TypeString, Optional: true, Description: "The DHCP client will use these as the default NTP servers. Two comma-separated NTP servers " + "can be specified to be used by the DHCP client as primary and secondary NTP servers", ValidateFunc: validation.IsIPv4Address, }, "wins_server": { Type: schema.TypeString, Optional: true, Description: "The Windows DHCP client will use these as the default WINS servers. Two comma-separated " + "WINS servers can be specified to be used by the DHCP client as primary and secondary WINS servers", ValidateFunc: validation.IsIPv4Address, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_dhcp_server_option.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* [ { ".id": "*4", "code": "66", "name": "optionname", "raw-value": "00002311", "value": "0x00002311" } ] */ // ResourceDhcpServerOption https://help.mikrotik.com/docs/display/ROS/DHCP#DHCP-DHCPServer func ResourceDhcpServerOption() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/dhcp-server/option"), MetaId: PropId(Id), KeyComment: PropCommentRw, "code": { Type: schema.TypeInt, Required: true, Description: "The number of the DHCP option", ValidateFunc: validation.IntBetween(1, 254), }, "force": { Type: schema.TypeBool, Optional: true, Description: "Force the DHCP option from the server-side even if the DHCP-client does not request such parameter.", }, "name": { Type: schema.TypeString, Required: true, Description: "The name of the DHCP option", }, "raw_value": { Type: schema.TypeString, Computed: true, Description: "The computed value of the option as an hex value", }, "value": { Type: schema.TypeString, Required: true, Description: "The value with formatting using Mikrotik settings https://wiki.mikrotik.com/wiki/Manual:IP/DHCP_Server#DHCP_Options", }, } return &schema.Resource{ Description: "Creates a DHCP lease on the mikrotik device.", CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_dhcp_server_option_matcher.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) // ResourceDhcpServerOptionMatcher https://help.mikrotik.com/docs/spaces/ROS/pages/24805500/DHCP#DHCP-Optionmatcher func ResourceDhcpServerOptionMatcher() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/dhcp-server/matcher"), MetaId: PropId(Id), "address_pool": { Type: schema.TypeString, Optional: true, Description: "IP pool, from which to take IP addresses for the clients. " + "If set to static-only, then only the clients that have a static " + "lease (added in lease submenu) will be allowed.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "code": { Type: schema.TypeInt, Required: true, Description: "DHCP option code. All codes are available at " + "http://www.iana.org/assignments/bootp-dhcp-parameters", ValidateFunc: validation.IntBetween(1, 254), }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "matching_type": { Type: schema.TypeString, Optional: true, Description: "Matching method:\n\n" + "- exact: option should match exactly to value\n" + "- substring: value can match anywhere in the option string; " + "at the start, middle, or end.", ValidateFunc: validation.StringInSlice( []string{"exact", "substring"}, false, ), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyName: PropNameForceNewRw, "option_set": { Type: schema.TypeString, Optional: true, Description: "A custom set of DHCP options defined in the Option Sets menu.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "server": { Type: schema.TypeString, Optional: true, Description: "Server name which serves option matcher.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "value": { Type: schema.TypeString, Optional: true, Description: "A value that will be searched for in option.\n" + "Available data types for value are:\n\n" + "- string\n" + "- HEX", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_dhcp_server_option_sets.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* [ { ".id": "*2", "name": "netboot", "options": "tftpserver-66,unifi,mtu-jumbo" } ] */ // ResourceDhcpServerOption https://wiki.mikrotik.com/wiki/Manual:IP/DHCP_Server func ResourceDhcpServerOptionSets() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/dhcp-server/option/sets"), MetaId: PropId(Id), KeyComment: PropCommentRw, "name": { Type: schema.TypeString, Required: true, Description: "The name of the DHCP option", }, "options": { Type: schema.TypeString, Required: true, Description: "The comma sepparated list of options", }, } return &schema.Resource{ Description: "Creates a DHCP lease on the mikrotik device.", CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_dhcp_server_option_sets_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpDhcpServerOptionSets = "routeros_ip_dhcp_server_option_sets.test_option_set" func TestAccIpDhcpServerNetworkOptionSet_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/dhcp-server/option/sets", "routeros_ip_dhcp_server_option_sets"), Steps: []resource.TestStep{ { Config: testAccIpDhcpServerOptionSetsConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpDhcpServerOptionSets), resource.TestCheckResourceAttr(testIpDhcpServerOptionSets, "name", "test-opt-set"), resource.TestCheckResourceAttr(testIpDhcpServerOptionSets, "options", "test-opt1,test-opt2"), ), }, }, }) }) } } func testAccIpDhcpServerOptionSetsConfig() string { return providerConfig + ` resource "routeros_ip_dhcp_server_option" "test_option_1" { code = 77 name = "test-opt1" value = "s'10.10.10.22'" } resource "routeros_ip_dhcp_server_option" "test_option_2" { code = 90 name = "test-opt2" value = "s'10.10.10.22'" } resource "routeros_ip_dhcp_server_option_sets" "test_option_set" { name = "test-opt-set" options = join(",", [routeros_ip_dhcp_server_option.test_option_1.name, routeros_ip_dhcp_server_option.test_option_2.name]) } ` } ================================================ FILE: routeros/resource_ip_dhcp_server_option_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpDhcpServerOption = "routeros_ip_dhcp_server_option.test_option" func TestAccIpDhcpServerNetworkOption_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/dhcp-server/option", "routeros_ip_dhcp_server_option"), Steps: []resource.TestStep{ { Config: testAccIpDhcpServerOptionConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpDhcpServerOption), resource.TestCheckResourceAttr(testIpDhcpServerOption, "code", "77"), resource.TestCheckResourceAttr(testIpDhcpServerOption, "name", "test-opt"), resource.TestCheckResourceAttr(testIpDhcpServerOption, "value", "s'10.10.10.22'"), resource.TestCheckResourceAttr(testIpDhcpServerOption, "raw_value", "31302e31302e31302e3232"), ), }, }, }) }) } } func testAccIpDhcpServerOptionConfig() string { return providerConfig + ` resource "routeros_ip_dhcp_server_option" "test_option" { code = 77 name = "test-opt" value = "s'10.10.10.22'" } ` } ================================================ FILE: routeros/resource_ip_dhcp_server_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpDhcpServerAddress = "routeros_ip_dhcp_server.test_dhcp" func TestAccIpDhcpServerTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/dhcp-server", "routeros_ip_dhcp_server"), Steps: []resource.TestStep{ { Config: testAccIpDhcpServerConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpDhcpServerAddress), resource.TestCheckResourceAttr(testIpDhcpServerAddress, "interface", "bridge"), ), }, }, }) }) } } func testAccIpDhcpServerConfig() string { return providerConfig + ` resource "routeros_ip_dhcp_server" "test_dhcp" { name = "test_dhcp_server" interface = "bridge" disabled = true address_pool = "dhcp" } ` } ================================================ FILE: routeros/resource_ip_dhcp_server_v0.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) func ResourceDhcpServerV0() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/dhcp-server"), MetaId: PropId(Name), "add_arp": { Type: schema.TypeBool, Optional: true, Description: "Whether to add dynamic ARP entry. ", }, "address_pool": { Type: schema.TypeString, Optional: true, Default: "static-only", Description: "IP pool, from which to take IP addresses for the clients. If set to static-only, then only " + "the clients that have a static lease (added in lease submenu) will be allowed.", }, "allow_dual_stack_queue": { Type: schema.TypeBool, Optional: true, Description: "Creates a single simple queue entry for both IPv4 and IPv6 addresses, uses the MAC address and " + "DUID for identification. Requires IPv6 DHCP Server to have this option enabled as well to work properly.", }, "always_broadcast": { Type: schema.TypeBool, Optional: true, Description: "Always send replies as broadcasts even if destination IP is known.", }, "authoritative": { Type: schema.TypeString, Optional: true, Default: "yes", Description: "Option changes the way how a server responds to DHCP requests.", ValidateFunc: validation.StringInSlice([]string{"after-10sec-delay", "after-2sec-delay", "yes", "no"}, false), }, "bootp_lease_time": { Type: schema.TypeString, Optional: true, Description: "Accepts two predefined options or time value: * forever - lease never expires " + "* lease-time - use time from lease-time parameter", }, "bootp_support": { Type: schema.TypeString, Optional: true, Default: "static", Description: "Support for BOOTP clients.", ValidateFunc: validation.StringInSlice([]string{"none", "static", "dynamic"}, false), }, "client_mac_limit": { Type: schema.TypeInt, Optional: true, Description: "Specifies whether to limit specific number of clients per single MAC address.", }, "conflict_detection": { Type: schema.TypeBool, Optional: true, Description: "Allows to disable/enable conflict detection. If option is enabled, then whenever server tries " + "to assign a lease it will send ICMP and ARP messages to detect whether such address in the network " + "already exist. If any of above get reply address is considered already used. Conflict detection must " + "be disabled when any kind of DHCP client limitation per port or per mac is used.", }, KeyComment: PropCommentRw, "delay_threshold": { Type: schema.TypeString, Optional: true, Description: "If secs field in DHCP packet is smaller than delay-threshold, then this packet is ignored. " + "If set to none - there is no threshold (all DHCP packets are processed).", }, "dhcp_option_set": { Type: schema.TypeString, Optional: true, Description: "Use custom set of DHCP options defined in option sets menu.", }, KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, "insert_queue_before": { Type: schema.TypeString, Optional: true, Description: "Specify where to place dynamic simple queue entries for static DCHP leases with rate-limit parameter set.", }, KeyInterface: PropInterfaceRw, KeyInvalid: PropInvalidRo, "lease_script": { Type: schema.TypeString, Optional: true, Description: "A script that will be executed after a lease is assigned or de-assigned.", }, "lease_time": { Type: schema.TypeString, Optional: true, Default: "10m", Description: "The time that a client may use the assigned address. The client will try to renew this " + "address after half of this time and will request a new address after the time limit expires.", }, KeyName: PropNameForceNewRw, "parent_queue": { Type: schema.TypeString, Optional: true, Description: "", }, "relay": { Type: schema.TypeString, Optional: true, Description: "The IP address of the relay this DHCP server.", ValidateFunc: ValidationIpAddress, }, "src_address": { Type: schema.TypeString, Optional: true, Description: "The address which the DHCP client must send requests to in order to renew an IP address lease.", ValidateFunc: ValidationIpAddress, }, "use_framed_as_classless": { Type: schema.TypeBool, Optional: true, Description: "Forward RADIUS Framed-Route as a DHCP Classless-Static-Route to DHCP-client.", }, "use_radius": { Type: schema.TypeString, Optional: true, Default: "no", Description: "Whether to use RADIUS server.", ValidateFunc: validation.StringInSlice([]string{"yes", "no", "accounting"}, false), }, }, } } ================================================ FILE: routeros/resource_ip_dns.go ================================================ package routeros import ( "context" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { "allow-remote-requests": "true", "cache-max-ttl": "1w", "cache-size": "2048", "cache-used": "99", RO "dynamic-servers": "", RO "max-concurrent-queries": "100", "max-concurrent-tcp-sessions": "20", "max-udp-packet-size": "4096", "query-server-timeout": "2s", "query-total-timeout": "10s", "servers": "192.168.1.1", "use-doh-server": "", "verify-doh-cert": "false" } */ // ResourceDns https://wiki.mikrotik.com/wiki/Manual:IP/DNS func ResourceDns() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/dns"), MetaId: PropId(Name), "address_list_extra_time": { Type: schema.TypeString, Optional: true, Description: "", ValidateFunc: ValidationTime, DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { return AlwaysPresentNotUserProvided(k, old, new, d) || TimeEqual(k, old, new, d) }, }, "allow_remote_requests": { Type: schema.TypeBool, Optional: true, Description: "Specifies whether to allow network requests.", }, "cache_max_ttl": { Type: schema.TypeString, Optional: true, Computed: true, Description: "Maximum time-to-live for cache records. In other words, cache records will expire " + "unconditionally after cache-max-ttl time. Shorter TTL received from DNS servers are respected. " + "*Default: 1w*", ValidateFunc: ValidationTime, DiffSuppressFunc: TimeEqual, }, "cache_size": { Type: schema.TypeInt, Optional: true, Computed: true, Description: "Specifies the size of DNS cache in KiB (64..4294967295). *Default: 2048*", }, "cache_used": { Type: schema.TypeInt, Computed: true, Description: "Shows the currently used cache size in KiB.", }, "doh_max_concurrent_queries": { Type: schema.TypeInt, Optional: true, Computed: true, Description: "Specifies how many DoH concurrent queries are allowed.", }, "doh_max_server_connections": { Type: schema.TypeInt, Optional: true, Computed: true, Description: "Specifies how many concurrent connections to the DoH server are allowed.", }, "doh_timeout": { Type: schema.TypeString, Optional: true, Computed: true, Description: "Specifies how long to wait for query response from the DoH server.", DiffSuppressFunc: TimeEqual, }, "dynamic_servers": { Type: schema.TypeString, Computed: true, Description: "List of dynamically added DNS server from different services, for example, DHCP.", }, "max_concurrent_queries": { Type: schema.TypeInt, Optional: true, Computed: true, Description: "Specifies how much concurrent queries are allowed. *Default: 100*", }, "max_concurrent_tcp_sessions": { Type: schema.TypeInt, Optional: true, Computed: true, Description: "Specifies how much concurrent TCP sessions are allowed. *Default: 20*", }, "max_udp_packet_size": { Type: schema.TypeInt, Optional: true, Computed: true, Description: "Maximum size of allowed UDP packet. *Default: 4096*", ValidateFunc: validation.IntBetween(50, 65507), }, "mdns_repeat_ifaces": { Type: schema.TypeSet, Optional: true, Description: "An option to enable mDNS repeater on specified interfaces. This option is available in RouterOS starting from version 7.16.", Elem: &schema.Schema{ Type: schema.TypeString, }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "query_server_timeout": { Type: schema.TypeString, Optional: true, Computed: true, Description: "Specifies how long to wait for query response from one server. " + "Time can be specified in milliseconds. *Default: 2s*", ValidateFunc: ValidationTime, DiffSuppressFunc: TimeEqual, }, "query_total_timeout": { Type: schema.TypeString, Optional: true, Computed: true, Description: "Specifies how long to wait for query response in total. Note that this setting must be " + "configured taking into account query_server_timeout and number of used DNS server. " + "Time can be specified in milliseconds. *Default: 10s*", ValidateFunc: ValidationTime, DiffSuppressFunc: TimeEqual, }, "servers": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "List of DNS server IPv4/IPv6 addresses.", }, KeyVrf: PropVrfRw, "use_doh_server": { Type: schema.TypeString, Optional: true, Description: `DNS over HTTPS (DoH) server URL. > Mikrotik strongly suggest not use third-party download links for certificate fetching. Use the Certificate Authority's own website. > RouterOS prioritize DoH over DNS server if both are configured on the device.`, }, "verify_doh_cert": { Type: schema.TypeBool, Optional: true, Description: "DoH certificate verification. [See docs](https://wiki.mikrotik.com/wiki/Manual:IP/DNS#DNS_over_HTTPS).", }, } return &schema.Resource{ Description: "A MikroTik router with DNS feature enabled can be set as a DNS server for any DNS-compliant client.", CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), // This behavior when deleting a system resource is the exception rather than the rule. // With existing serialization logic, the best way to avoid undefined DNS service state // is to clear the main fields. DeleteContext: func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { // Values in the Mikrotik notation! resetFileds := map[string]string{ "allow-remote-requests": "no", "servers": "", "use-doh-server": "", "verify-doh-cert": "no", } var resUrl string if m.(Client).GetTransport() == TransportREST { // https://router/rest/ip/dns/set resUrl = "/set" } // Used POST request! err := m.(Client).SendRequest(crudPost, &URL{Path: resSchema[MetaResourcePath].Default.(string) + resUrl}, resetFileds, nil) if err != nil { return diag.FromErr(err) } d.SetId("") return nil }, Schema: resSchema, SchemaVersion: 1, StateUpgraders: []schema.StateUpgrader{ { Type: ResourceDnsV0().CoreConfigSchema().ImpliedType(), Upgrade: stateMigrationScalarToList("servers"), Version: 0, }, }, } } ================================================ FILE: routeros/resource_ip_dns_adlist.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*1", "disabled": "false", "match-count": "0", "name-count": "0", "url": "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts" } */ // ResourceDnsAdlist https://help.mikrotik.com/docs/display/ROS/DNS#DNS-Adlist func ResourceDnsAdlist() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/dns/adlist"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("match_count", "name_count"), KeyDisabled: PropDisabledRw, "file": { Type: schema.TypeString, Optional: true, Description: "Used to specify a local file path from which to read adlist data.", ExactlyOneOf: []string{"file", "url"}, }, "ssl_verify": { Type: schema.TypeBool, Optional: true, Description: "Specifies whether to validate the server's SSL certificate when connecting to an online " + "resource. Will use the `/certificate` list to verify server validity.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "url": { Type: schema.TypeString, Optional: true, Description: "Used to specify the URL of an adlist.", ExactlyOneOf: []string{"file", "url"}, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_dns_adlist_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testDnsAdlistMinVersion = "7.15" const testResourceDnsAdlist = "routeros_ip_dns_adlist.test" func TestAccResourceDnsAdlistTest_basic(t *testing.T) { if !testCheckMinVersion(t, testDnsAdlistMinVersion) { t.Logf("Test skipped, the minimum required version is %v", testDnsAdlistMinVersion) return } for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/dns/adlist", "routeros_ip_dns_adlist"), Steps: []resource.TestStep{ { Config: testAccResourceDnsAdlistConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testResourceDnsAdlist), resource.TestCheckResourceAttr(testResourceDnsAdlist, "url", "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts"), resource.TestCheckResourceAttr(testResourceDnsAdlist, "ssl_verify", "false"), ), }, }, }) }) } } func testAccResourceDnsAdlistConfig() string { return providerConfig + ` resource "routeros_ip_dns_adlist" "test" { url = "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts" ssl_verify = false }` } ================================================ FILE: routeros/resource_ip_dns_forwarders.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*1", "disabled": "false", "dns-servers": "1.1.1.1", "doh-servers": "2.2.2.2", "name": "aaa", "verify-doh-cert": "false" } */ // https://help.mikrotik.com/docs/spaces/ROS/pages/37748767/DNS#DNS-Forwarders func ResourceIpDnsForwarders() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/dns/forwarders"), MetaId: PropId(Id), KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "dns_servers": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "An IP address or DNS name of a domain name server. Can contain multiple records.", }, "doh_servers": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "A URL of DoH server. Can contain multiple records.", }, KeyName: PropName("Forwarder name."), "verify_doh_cert": { Type: schema.TypeBool, Optional: true, Description: "Specifies whether to validate the DoH server, when one is being used. Will use the `/certificate` " + "list in order to verify server validity.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_dns_forwarders_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpDnsForwardersMinVersion = "7.17" const testIpDnsForwarders = "routeros_ip_dns_forwarders.test" func TestAccIpDnsForwardersTest_basic(t *testing.T) { if !testCheckMinVersion(t, testIpDnsForwardersMinVersion) { t.Logf("Test skipped, the minimum required version is %v", testIpDnsForwardersMinVersion) return } t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/dns/forwarders", "routeros_ip_dns_forwarders"), Steps: []resource.TestStep{ { Config: testAccIpDnsForwardersConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpDnsForwarders), resource.TestCheckResourceAttr(testIpDnsForwarders, "disabled", "true"), resource.TestCheckResourceAttr(testIpDnsForwarders, "dns_servers.0", "1.1.1.1"), resource.TestCheckResourceAttr(testIpDnsForwarders, "doh_servers.0", "2.2.2.2"), resource.TestCheckResourceAttr(testIpDnsForwarders, "name", "test"), resource.TestCheckResourceAttr(testIpDnsForwarders, "verify_doh_cert", "false"), ), }, }, }) }) } } func testAccIpDnsForwardersConfig() string { return fmt.Sprintf(`%v resource "routeros_ip_dns_forwarders" "test" { disabled = true dns_servers = ["1.1.1.1"] doh_servers = ["2.2.2.2"] name = "test" verify_doh_cert = "false" } `, providerConfig) } ================================================ FILE: routeros/resource_ip_dns_record.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { { { ".id": "*3", ".id": "*5", ".id": "*4", "address": "127.0.0.1", "address": "ff00::1", "cname": "ipv4", "address-list": "subdomain", "disabled": "false", "disabled": "false", "disabled": "false", "dynamic": "false", "dynamic": "false", "dynamic": "false", "name": "ipv6", "name": "cname", "match-subdomain": "true", "ttl": "1d", "ttl": "1d", "name": "ipv4", "type": "AAAA" "type": "CNAME" "ttl": "1d" }, }, }, { { { ".id": "*6", ".id": "*7", ".id": "*8", "disabled": "false", "disabled": "true", "disabled": "false", "dynamic": "false", "dynamic": "false", "dynamic": "false", "forward-to": "127.0.0.1", "mx-exchange": "127.0.0.1", "name": "ns", "name": "fwd", "mx-preference": "10", "ns": "127.0.0.1", "ttl": "1d", "name": "mx", "ttl": "1d", "type": "FWD" "ttl": "1d", "type": "NS" }, "type": "MX" }, }, { { { ".id": "*9", ".id": "*A", ".id": "*B", "disabled": "false", "disabled": "false", "disabled": "false", "dynamic": "false", "dynamic": "false", "dynamic": "false", "name": "nxdomain", "name": "srv", "name": "txt", "ttl": "1d", "srv-port": "8080", "text": "DNSSEC", "type": "NXDOMAIN" "srv-priority": "10", "ttl": "1d", }, "srv-target": "127.0.0.1", "type": "TXT" "srv-weight": "100", } "ttl": "1d", "type": "SRV" }, */ // ResourceDnsRecord https://wiki.mikrotik.com/wiki/Manual:IP/DNS https://help.mikrotik.com/docs/display/ROS/DNS func ResourceDnsRecord() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/dns/static"), MetaId: PropId(Id), "address": { Type: schema.TypeString, Optional: true, Description: "The A record to be returend from the DNS hostname.", ConflictsWith: []string{"cname", "forward_to", "mx_exchange", "ns", "srv_target", "text"}, ValidateFunc: validation.IsIPAddress, }, "address_list": { Type: schema.TypeString, Optional: true, Description: "Name of the Firewall address list to which address must be dynamically added when some " + "request matches the entry.", }, KeyComment: PropCommentRw, "cname": { Type: schema.TypeString, Optional: true, Description: "Alias name for a domain name.", ConflictsWith: []string{"address", "forward_to", "mx_exchange", "ns", "srv_target", "text"}, }, KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, "forward_to": { Type: schema.TypeString, Optional: true, Description: "The IP address of a domain name server to which a particular DNS request must be forwarded.", ConflictsWith: []string{"address", "cname", "mx_exchange", "ns", "srv_target", "text"}, }, "match_subdomain": { Type: schema.TypeBool, Optional: true, Description: "Whether the record will match requests for subdomains.", }, "mx_exchange": { Type: schema.TypeString, Optional: true, Description: "The domain name of the MX server.", ConflictsWith: []string{"address", "cname", "forward_to", "ns", "srv_target", "text"}, }, "mx_preference": { Type: schema.TypeInt, Optional: true, Computed: true, Description: "Preference of the particular MX record.", RequiredWith: []string{"mx_exchange"}, }, "name": { Type: schema.TypeString, Optional: true, Description: "The name of the DNS hostname to be created.", ExactlyOneOf: []string{"name", "regexp"}, }, "ns": { Type: schema.TypeString, Optional: true, Description: "Name of the authoritative domain name server for the particular record.", ConflictsWith: []string{"address", "cname", "forward_to", "mx_exchange", "srv_target", "text"}, }, "regexp": { Type: schema.TypeString, Optional: true, Description: "DNS regexp. Regexp entries are case sensitive, but since DNS requests are not case sensitive, " + "RouterOS converts DNS names to lowercase, you should write regex only with lowercase letters.", ExactlyOneOf: []string{"name", "regexp"}, }, "srv_port": { Type: schema.TypeInt, Optional: true, Computed: true, Description: "The TCP or UDP port on which the service is to be found.", ValidateFunc: Validation64k, RequiredWith: []string{"srv_target"}, }, "srv_priority": { Type: schema.TypeInt, Optional: true, Computed: true, Description: "Priority of the particular SRV record.", RequiredWith: []string{"srv_target"}, }, "srv_target": { Type: schema.TypeString, Optional: true, Description: "The canonical hostname of the machine providing the service ends in a dot.", ConflictsWith: []string{"address", "cname", "forward_to", "mx_exchange", "ns", "text"}, }, "srv_weight": { Type: schema.TypeString, Optional: true, Computed: true, Description: "Weight of the particular SRC record.", RequiredWith: []string{"srv_target"}, }, "text": { Type: schema.TypeString, Optional: true, Description: "Textual information about the domain name.", ConflictsWith: []string{"address", "cname", "forward_to", "mx_exchange", "ns", "srv_target"}, }, "ttl": { Type: schema.TypeString, Optional: true, Description: "The ttl of the DNS record.", DiffSuppressFunc: TimeEqual, }, "type": { Type: schema.TypeString, Required: true, ForceNew: true, Description: "Type of the DNS record. Available values are: A, AAAA, CNAME, FWD, MX, NS, NXDOMAIN, SRV, TXT", ValidateFunc: validation.StringInSlice([]string{"A", "AAAA", "CNAME", "FWD", "MX", "NS", "NXDOMAIN", "SRV", "TXT"}, false), }, } return &schema.Resource{ Description: "Creates a DNS record on the MikroTik device.", CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_dns_record_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) func TestAccIpDnsRecordTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/dns/static", "routeros_dns_record"), Steps: []resource.TestStep{ { Config: testAccIpDnsRecordConfig(), Check: resource.ComposeTestCheckFunc( // A testResourcePrimaryInstanceId("routeros_dns_record.test_dns_a"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_a", "name", "ipv4"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_a", "address", "127.0.0.1"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_a", "type", "A"), // A regexp testResourcePrimaryInstanceId("routeros_dns_record.test_dns_a_regexp"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_a", "address", "127.0.0.1"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_a_regexp", "regexp", "regexp"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_a_regexp", "type", "A"), // AAAA testResourcePrimaryInstanceId("routeros_dns_record.test_dns_aaaa"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_aaaa", "name", "ipv6"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_aaaa", "address", "ff00::1"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_aaaa", "type", "AAAA"), // CNAME testResourcePrimaryInstanceId("routeros_dns_record.test_dns_cname"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_cname", "cname", "ipv4"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_cname", "name", "cname"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_cname", "type", "CNAME"), // FWD testResourcePrimaryInstanceId("routeros_dns_record.test_dns_fwd"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_fwd", "name", "fwd"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_fwd", "forward_to", "127.0.0.1"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_fwd", "type", "FWD"), // MX testResourcePrimaryInstanceId("routeros_dns_record.test_dns_mx"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_mx", "name", "mx"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_mx", "mx_exchange", "127.0.0.1"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_mx", "mx_preference", "10"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_mx", "type", "MX"), // NS testResourcePrimaryInstanceId("routeros_dns_record.test_dns_ns"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_ns", "name", "ns"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_ns", "ns", "127.0.0.1"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_ns", "type", "NS"), // NXDOMAIN testResourcePrimaryInstanceId("routeros_dns_record.test_dns_nxdomain"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_nxdomain", "name", "nxdomain"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_nxdomain", "type", "NXDOMAIN"), // SRV testResourcePrimaryInstanceId("routeros_dns_record.test_dns_srv"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_srv", "name", "srv"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_srv", "srv_port", "8080"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_srv", "srv_priority", "10"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_srv", "srv_target", "127.0.0.1"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_srv", "srv_weight", "100"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_srv", "type", "SRV"), // TXT testResourcePrimaryInstanceId("routeros_dns_record.test_dns_txt"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_txt", "name", "_acme-challenge.yourwebsite.com"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_txt", "text", "dW6MrI3nBy3eJgYWH3QAg1Cwk_TvjFESOuKo+mp6nm1"), resource.TestCheckResourceAttr("routeros_dns_record.test_dns_txt", "type", "TXT"), ), }, }, }) }) } } func testAccIpDnsRecordConfig() string { return providerConfig + ` resource "routeros_dns_record" "test_dns_a" { address = "127.0.0.1" address_list = "subdomain" disabled = false match_subdomain = true name = "ipv4" ttl = "8m" type = "A" } resource "routeros_dns_record" "test_dns_a_regexp" { address = "127.0.0.1" disabled = true regexp = "regexp" type = "A" } resource "routeros_dns_record" "test_dns_aaaa" { address = "ff00::1" address_list = "subdomain" disabled = false match_subdomain = true name = "ipv6" ttl = "8m" type = "AAAA" } resource "routeros_dns_record" "test_dns_cname" { address_list = "subdomain" cname = "ipv4" disabled = false match_subdomain = true name = "cname" ttl = "8m" type = "CNAME" } resource "routeros_dns_record" "test_dns_fwd" { address_list = "subdomain" disabled = false forward_to = "127.0.0.1" match_subdomain = true name = "fwd" ttl = "8m" type = "FWD" } resource "routeros_dns_record" "test_dns_mx" { address_list = "subdomain" disabled = false match_subdomain = true mx_exchange = "127.0.0.1" mx_preference = 10 name = "mx" ttl = "8m" type = "MX" } resource "routeros_dns_record" "test_dns_ns" { address_list = "subdomain" disabled = false match_subdomain = true name = "ns" ns = "127.0.0.1" ttl = "8m" type = "NS" } resource "routeros_dns_record" "test_dns_nxdomain" { address_list = "subdomain" disabled = false match_subdomain = true name = "nxdomain" ttl = "8m" type = "NXDOMAIN" } resource "routeros_dns_record" "test_dns_srv" { address_list = "subdomain" disabled = false match_subdomain = true name = "srv" srv_port = 8080 srv_priority = 10 srv_target = "127.0.0.1" srv_weight = 100 ttl = "8m" type = "SRV" } resource "routeros_dns_record" "test_dns_txt" { address_list = "subdomain" disabled = false match_subdomain = true name = "_acme-challenge.yourwebsite.com" text = "dW6MrI3nBy3eJgYWH3QAg1Cwk_TvjFESOuKo+mp6nm1" ttl = "8m" type = "TXT" } ` } ================================================ FILE: routeros/resource_ip_dns_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testResourceDnsTask = "routeros_dns.test" func TestAccResourceDnsTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccResourceDnsConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testResourceDnsTask), resource.TestCheckResourceAttr(testResourceDnsTask, "allow_remote_requests", "true"), ), }, { Config: testAccResourceDnsConfig(), }, }, }) }) } } func testAccResourceDnsConfig() string { return providerConfig + ` resource "routeros_dns" "test" { allow_remote_requests = true cache_max_ttl = "3d" cache_size = 4096 max_concurrent_queries = 200 max_concurrent_tcp_sessions = 40 max_udp_packet_size = 8192 query_server_timeout = "500ms" query_total_timeout = "15" servers = [ "2606:4700:4700::1112", "1.1.1.2", "2606:4700:4700::1002", "1.0.0.2", ] use_doh_server = "https://cloudflare-dns.com/dns-query" verify_doh_cert = true }` } ================================================ FILE: routeros/resource_ip_dns_v0.go ================================================ package routeros import ( "context" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { "allow-remote-requests": "true", "cache-max-ttl": "1w", "cache-size": "2048", "cache-used": "99", RO "dynamic-servers": "", RO "max-concurrent-queries": "100", "max-concurrent-tcp-sessions": "20", "max-udp-packet-size": "4096", "query-server-timeout": "2s", "query-total-timeout": "10s", "servers": "192.168.1.1", "use-doh-server": "", "verify-doh-cert": "false" } */ // ResourceDns https://wiki.mikrotik.com/wiki/Manual:IP/DNS func ResourceDnsV0() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/dns"), MetaId: PropId(Name), "address_list_extra_time": { Type: schema.TypeString, Optional: true, Description: "", ValidateFunc: ValidationTime, DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { return AlwaysPresentNotUserProvided(k, old, new, d) || TimeEqual(k, old, new, d) }, }, "allow_remote_requests": { Type: schema.TypeBool, Optional: true, Description: "Specifies whether to allow network requests.", }, "cache_max_ttl": { Type: schema.TypeString, Optional: true, Computed: true, Description: "Maximum time-to-live for cache records. In other words, cache records will expire " + "unconditionally after cache-max-ttl time. Shorter TTL received from DNS servers are respected. " + "*Default: 1w*", ValidateFunc: ValidationTime, DiffSuppressFunc: TimeEqual, }, "cache_size": { Type: schema.TypeInt, Optional: true, Computed: true, Description: "Specifies the size of DNS cache in KiB (64..4294967295). *Default: 2048*", }, "cache_used": { Type: schema.TypeInt, Computed: true, Description: "Shows the currently used cache size in KiB.", }, "doh_max_concurrent_queries": { Type: schema.TypeInt, Optional: true, Computed: true, Description: "Specifies how many DoH concurrent queries are allowed.", }, "doh_max_server_connections": { Type: schema.TypeInt, Optional: true, Computed: true, Description: "Specifies how many concurrent connections to the DoH server are allowed.", }, "doh_timeout": { Type: schema.TypeString, Optional: true, Computed: true, Description: "Specifies how long to wait for query response from the DoH server.", DiffSuppressFunc: TimeEqual, }, "dynamic_servers": { Type: schema.TypeString, Computed: true, Description: "List of dynamically added DNS server from different services, for example, DHCP.", }, "max_concurrent_queries": { Type: schema.TypeInt, Optional: true, Computed: true, Description: "Specifies how much concurrent queries are allowed. *Default: 100*", }, "max_concurrent_tcp_sessions": { Type: schema.TypeInt, Optional: true, Computed: true, Description: "Specifies how much concurrent TCP sessions are allowed. *Default: 20*", }, "max_udp_packet_size": { Type: schema.TypeInt, Optional: true, Computed: true, Description: "Maximum size of allowed UDP packet. *Default: 4096*", ValidateFunc: validation.IntBetween(50, 65507), }, "query_server_timeout": { Type: schema.TypeString, Optional: true, Computed: true, Description: "Specifies how long to wait for query response from one server. " + "Time can be specified in milliseconds. *Default: 2s*", ValidateFunc: ValidationTime, DiffSuppressFunc: TimeEqual, }, "query_total_timeout": { Type: schema.TypeString, Optional: true, Computed: true, Description: "Specifies how long to wait for query response in total. Note that this setting must be " + "configured taking into account query_server_timeout and number of used DNS server. " + "Time can be specified in milliseconds. *Default: 10s*", ValidateFunc: ValidationTime, DiffSuppressFunc: TimeEqual, }, "servers": { Type: schema.TypeString, Optional: true, Description: "List of DNS server IPv4/IPv6 addresses.", }, KeyVrf: PropVrfRw, "use_doh_server": { Type: schema.TypeString, Optional: true, Description: `DNS over HTTPS (DoH) server URL. > Mikrotik strongly suggest not use third-party download links for certificate fetching. Use the Certificate Authority's own website. > RouterOS prioritize DoH over DNS server if both are configured on the device.`, }, "verify_doh_cert": { Type: schema.TypeBool, Optional: true, Description: "DoH certificate verification. [See docs](https://wiki.mikrotik.com/wiki/Manual:IP/DNS#DNS_over_HTTPS).", }, } return &schema.Resource{ Description: "A MikroTik router with DNS feature enabled can be set as a DNS server for any DNS-compliant client.", CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), // This behavior when deleting a system resource is the exception rather than the rule. // With existing serialization logic, the best way to avoid undefined DNS service state // is to clear the main fields. DeleteContext: func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { // Values in the Mikrotik notation! resetFileds := map[string]string{ "allow-remote-requests": "no", "servers": "", "use-doh-server": "", "verify-doh-cert": "no", } var resUrl string if m.(Client).GetTransport() == TransportREST { // https://router/rest/ip/dns/set resUrl = "/set" } // Used POST request! err := m.(Client).SendRequest(crudPost, &URL{Path: resSchema[MetaResourcePath].Default.(string) + resUrl}, resetFileds, nil) if err != nil { return diag.FromErr(err) } d.SetId("") return nil }, Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_firewall_addr_list.go ================================================ package routeros import ( "regexp" "time" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*1", "address": "0.0.0.0", "creation-time": "sep/29/2022 07:09:24", "disabled": "false", "dynamic": "true", "list": "AAA", "timeout": "0s" } */ // ResourceIPFirewallAddrList https://wiki.mikrotik.com/wiki/Manual:IP/Firewall/Address_list func ResourceIPFirewallAddrList() *schema.Resource { var reIPv4Range = regexp.MustCompile(`(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s*-\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})`) resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/firewall/address-list"), MetaId: PropId(Id), "address": { Type: schema.TypeString, Required: true, Description: "A single IP address or range of IPs to add to address list or DNS name. You can input for " + "example, '192.168.0.0-192.168.1.255' and it will auto modify the typed entry to 192.168.0.0/23 on " + "saving.", DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { if old == new { return true } if old == "" || new == "" { return false } ips := reIPv4Range.FindStringSubmatch(new) if len(ips) == 3 { s, _ := IpRangeToCIDR(ips[1], ips[2]) return old == s } return false }, }, KeyComment: PropCommentRw, "creation_time": { Type: schema.TypeString, Computed: true, Description: "Rule creation time", }, KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, "list": { Type: schema.TypeString, Required: true, Description: "Name for the address list of the added IP address.", }, "timeout": { Type: schema.TypeString, Optional: true, Description: `Time after address will be removed from address list. If timeout is not specified, the address will be stored into the address list permanently. > Please plan your work logic based on the fact that after the timeout > the resource has been destroyed outside of a Terraform. `, DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { if old == new { return true } if old == "" || new == "" { return false } // Compare intervals: oDuration, err := ParseDuration(old, time.Second) if err != nil { panic("[FirewallAddrList Timeout] parse 'old' duration error: " + err.Error()) } nDuration, err := ParseDuration(new, time.Second) if err != nil { panic("[FirewallAddrList Timeout] parse 'new' duration error: " + err.Error()) } // old new // ~ timeout = "4m59s" -> "5m" return nDuration.Seconds() > oDuration.Seconds() }, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_firewall_addr_list_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIPFirewallrAddrList = "routeros_firewall_addr_list.data" func TestAccIPFirewallAddrListTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/firewall/address-list", "routeros_firewall_addr_list"), Steps: []resource.TestStep{ { Config: testAccIPFirewallAddrListConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIPFirewallrAddrList), resource.TestCheckResourceAttr(testIPFirewallrAddrList, "list", "test-addr-list"), resource.TestCheckResourceAttr(testIPFirewallrAddrList, "address", "192.168.0.0/23"), ), }, }, }) }) } } func testAccIPFirewallAddrListConfig() string { return providerConfig + ` resource "routeros_firewall_addr_list" "data" { list = "test-addr-list" address = "192.168.0.0-192.168.1.255" timeout = "5m" } ` } ================================================ FILE: routeros/resource_ip_firewall_connection_tracking.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { "active-ipv4": "yes", "active-ipv6": "yes", "enabled": "yes", "generic-timeout": "10m", "icmp-timeout": "10s", "loose-tcp-tracking": "true", "max-entries": "1048576", "tcp-close-timeout": "1m", "tcp-close-wait-timeout": "1m", "tcp-established-timeout": "1d", "tcp-fin-wait-timeout": "1m", "tcp-last-ack-timeout": "1m", "tcp-max-retrans-timeout": "5m", "tcp-syn-received-timeout": "5s", "tcp-syn-sent-timeout": "5s", "tcp-time-wait-timeout": "1m", "tcp-unacked-timeout": "5m", "total-entries": "87", "total-ip4-entries: "499", "total-ip6-entries: "1", "udp-stream-timeout": "3m", "udp-timeout": "10s" } */ // ResourceIPConnectionTracking https://help.mikrotik.com/docs/display/ROS/Connection+tracking func ResourceIPConnectionTracking() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/firewall/connection/tracking"), MetaId: PropId(Name), MetaSkipFields: PropSkipFields("total_entries", "total_ip4_entries", "total_ip6_entries"), "active_ipv4": { Type: schema.TypeBool, Computed: true, Description: "documentation is missing", }, "active_ipv6": { Type: schema.TypeBool, Computed: true, Description: "documentation is missing", }, "enabled": { Type: schema.TypeString, Optional: true, Description: `Allows to disable or enable connection tracking. Disabling connection tracking will cause several firewall features to stop working. See the list of affected features. Starting from v6.0rc2 default value is auto. This means that connection tracing is disabled until at least one firewall rule is added.`, ValidateFunc: ValidationAutoYesNo, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "generic_timeout": { Type: schema.TypeString, Optional: true, Description: "Timeout for all other connection entries", ValidateFunc: ValidationTime, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "icmp_timeout": { Type: schema.TypeString, Optional: true, Description: "ICMP connection timeout", ValidateFunc: ValidationTime, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "liberal_tcp_tracking": { Type: schema.TypeBool, Optional: true, Description: "Enables or disables liberal TCP connection tracking by toggling the kernel parameter " + "nf_conntrack_tcp_be_liberal. When set to `yes`, the system mark only out of window RST segments as INVALID." + "\n\n`Enabling this setting may allow malformed packets that would otherwise be considered invalid by the " + "firewall's connection-state matcher. This can increase exposure to certain evasion techniques. This " + "property should be enabled only when troubleshooting or working around known issues.`", }, "loose_tcp_tracking": { Type: schema.TypeString, Optional: true, Description: "Disable picking up already established connections", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "max_entries": { Type: schema.TypeString, Description: `Max amount of entries that the connection tracking table can hold. This value depends on the installed amount of RAM. Note that the system does not create a maximum_size connection tracking table when it starts, it may increase if the situation demands it and the system still has free ram, but size will not exceed 1048576`, Computed: true, }, "tcp_close_timeout": { Type: schema.TypeString, Optional: true, Description: "No documentation", ValidateFunc: ValidationTime, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "tcp_close_wait_timeout": { Type: schema.TypeString, Optional: true, Description: "No documentation", ValidateFunc: ValidationTime, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "tcp_established_timeout": { Type: schema.TypeString, Optional: true, Description: "Time when established TCP connection times out.", ValidateFunc: ValidationTime, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "tcp_fin_wait_timeout": { Type: schema.TypeString, Optional: true, Description: "No documentation", ValidateFunc: ValidationTime, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "tcp_last_ack_timeout": { Type: schema.TypeString, Optional: true, Description: "No documentation", ValidateFunc: ValidationTime, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "tcp_max_retrans_timeout": { Type: schema.TypeString, Optional: true, // Documentation did contain the default, I'm getting it from the docker image default (7.10) Description: "No documentation", ValidateFunc: ValidationTime, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "tcp_syn_received_timeout": { Type: schema.TypeString, Optional: true, Description: "TCP SYN timeout.", ValidateFunc: ValidationTime, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "tcp_syn_sent_timeout": { Type: schema.TypeString, Optional: true, Description: "TCP SYN timeout.", ValidateFunc: ValidationTime, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "tcp_time_wait_timeout": { Type: schema.TypeString, Optional: true, Description: "No documentation", ValidateFunc: ValidationTime, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "tcp_unacked_timeout": { Type: schema.TypeString, Optional: true, Description: "No documentation", ValidateFunc: ValidationTime, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "udp_stream_timeout": { Type: schema.TypeString, Optional: true, Description: "Specifies the timeout of UDP connections that has seen packets in both directions", ValidateFunc: ValidationTime, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "udp_timeout": { Type: schema.TypeString, Optional: true, Description: "Specifies the timeout for UDP connections that have seen packets in one direction", ValidateFunc: ValidationTime, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_firewall_connection_tracking_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIPConnectionTracking = "routeros_ip_firewall_connection_tracking.data" const testMingIPConnTrackingVersion = "7.10" func TestAccIPConnectionTrackingTest_basic(t *testing.T) { for _, name := range testNames { if !testCheckMinVersion(t, testMingIPConnTrackingVersion) { t.Logf("Test skipped, the minimum required version is %v", testMingIPConnTrackingVersion) return } t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ // we can set all fields to non default { Config: testAccIPConnectionTrackingFullConfig(), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(testIPConnectionTracking, "active_ipv4", "true"), resource.TestCheckResourceAttr(testIPConnectionTracking, "active_ipv6", "true"), resource.TestCheckResourceAttr(testIPConnectionTracking, "enabled", "yes"), resource.TestCheckResourceAttr(testIPConnectionTracking, "generic_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "icmp_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "loose_tcp_tracking", "false"), // resource.TestCheckResourceAttr(testIPConnectionTracking, "max_entries", "419840"), ROS 7.16 - 337920 resource.TestCheckResourceAttr(testIPConnectionTracking, "tcp_close_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "tcp_close_wait_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "tcp_established_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "tcp_fin_wait_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "tcp_last_ack_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "tcp_max_retrans_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "tcp_syn_received_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "tcp_syn_sent_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "tcp_time_wait_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "tcp_unacked_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "udp_stream_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "udp_timeout", "3m"), ), }, // Empty resource don't override the settings { Config: testAccIPConnectionTrackingEmptyConfig(), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(testIPConnectionTracking, "active_ipv4", "true"), resource.TestCheckResourceAttr(testIPConnectionTracking, "active_ipv6", "true"), resource.TestCheckResourceAttr(testIPConnectionTracking, "enabled", "yes"), resource.TestCheckResourceAttr(testIPConnectionTracking, "generic_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "icmp_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "loose_tcp_tracking", "false"), // resource.TestCheckResourceAttr(testIPConnectionTracking, "max_entries", "419840"), ROS 7.16 - 337920 resource.TestCheckResourceAttr(testIPConnectionTracking, "tcp_close_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "tcp_close_wait_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "tcp_established_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "tcp_fin_wait_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "tcp_last_ack_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "tcp_max_retrans_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "tcp_syn_received_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "tcp_syn_sent_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "tcp_time_wait_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "tcp_unacked_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "udp_stream_timeout", "3m"), resource.TestCheckResourceAttr(testIPConnectionTracking, "udp_timeout", "3m"), ), }, }, }) }) } } func testAccIPConnectionTrackingEmptyConfig() string { return providerConfig + ` resource "routeros_ip_firewall_connection_tracking" "data" { } ` } func testAccIPConnectionTrackingFullConfig() string { return providerConfig + ` resource "routeros_ip_firewall_connection_tracking" "data" { enabled = "yes" generic_timeout = "3m" icmp_timeout = "3m" loose_tcp_tracking = "false" tcp_close_timeout = "3m" tcp_close_wait_timeout = "3m" tcp_established_timeout = "3m" tcp_fin_wait_timeout = "3m" tcp_last_ack_timeout = "3m" tcp_max_retrans_timeout = "3m" tcp_syn_received_timeout = "3m" tcp_syn_sent_timeout = "3m" tcp_time_wait_timeout = "3m" tcp_unacked_timeout = "3m" udp_stream_timeout = "3m" udp_timeout = "3m" } ` } ================================================ FILE: routeros/resource_ip_firewall_filter.go ================================================ package routeros import ( "context" "regexp" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) // ResourceIPFirewallFilter https://wiki.mikrotik.com/wiki/Manual:IP/Firewall/Filter func ResourceIPFirewallFilter() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/firewall/filter"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("bytes", "packets"), MetaSetUnsetFields: PropSetUnsetFields("dst_address", "dst_address_list", "src_address", "src_address_list", "in_interface", "in_interface_list", "out_interface", "out_interface_list", "in_bridge_port_list", "out_bridge_port_list", "protocol"), "action": { Type: schema.TypeString, Required: true, Description: "Action to take if a packet is matched by the rule", ValidateFunc: validation.StringInSlice([]string{ "accept", "add-dst-to-address-list", "add-src-to-address-list", "drop", "fasttrack-connection", "jump", "log", "passthrough", "reject", "return", "tarpit", }, false), }, "address_list": { Type: schema.TypeString, Optional: true, Description: "Name of the address list used in 'add-dst-to-address-list' and 'add-src-to-address-list' actions.", }, "address_list_timeout": { Type: schema.TypeString, Optional: true, Description: "Time interval after which the address will be removed from the address list specified by " + "address-list parameter. Used in conjunction with add-dst-to-address-list or add-src-to-address-list " + "actions.", DiffSuppressFunc: TimeEqual, }, "chain": { Type: schema.TypeString, Required: true, Description: "Specifies to which chain rule will be added. If the input does not match the name of an " + "already defined chain, a new chain will be created.", }, KeyComment: PropCommentRw, "connection_bytes": { Type: schema.TypeString, Optional: true, Description: "Matches packets only if a given amount of bytes has been transfered through the particular " + "connection.", }, "connection_limit": { Type: schema.TypeString, Optional: true, Description: "Matches connections per address or address block after given value is reached. Should be " + "used together with connection-state=new and/or with tcp-flags=syn because matcher is very resource " + "intensive.", }, "connection_mark": { Type: schema.TypeString, Optional: true, Description: "Matches packets marked via mangle facility with particular connection mark. If no-mark is " + "set, rule will match any unmarked connection.", }, "connection_nat_state": { Type: schema.TypeString, Optional: true, Description: "Can match connections that are srcnatted, dstnatted or both.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"srcnat", "dstnat"}, false, true), }, // See comment for the "path_cost" field in resource_interface_bridge_port.go file. "connection_rate": { Type: schema.TypeString, Optional: true, Description: "Connection Rate is a firewall matcher that allow to capture traffic based on present speed " + "of the connection (0..4294967295).", }, "connection_state": { Type: schema.TypeString, Optional: true, Description: "Interprets the connection tracking analysis data for a particular packet.", ValidateDiagFunc: ValidationMultiValInSlice([]string{ "established", "invalid", "new", "related", "untracked", }, false, true), }, "connection_type": { Type: schema.TypeString, Optional: true, Description: "Matches packets from related connections based on information from their connection " + "tracking helpers.", ValidateDiagFunc: ValidationMultiValInSlice([]string{ "ftp", "h323", "irc", "pptp", "quake3", "sip", "tftp", }, false, true), }, "content": { Type: schema.TypeString, Optional: true, Description: "Match packets that contain specified text.", }, KeyDisabled: PropDisabledRw, "dscp": { Type: schema.TypeInt, Optional: true, Description: "Matches DSCP IP header field.", ValidateFunc: validation.IntBetween(0, 63), }, "dst_address": { Type: schema.TypeString, Optional: true, Description: "Matches packets which destination is equal to specified IP or falls into specified IP range.", DiffSuppressFunc: ImplicitSingleHostCIDR4, }, "dst_address_list": { Type: schema.TypeString, Optional: true, Description: "Matches destination address of a packet against user-defined address list.", }, "dst_address_type": { Type: schema.TypeString, Optional: true, Description: "Matches destination address type.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"unicast", "local", "broadcast", "multicast"}, false, true), }, "dst_limit": { Type: schema.TypeString, Optional: true, Description: "Matches packets until a given rate is exceeded.", }, "dst_port": { Type: schema.TypeString, Optional: true, Description: "List of destination port numbers or port number ranges.", }, KeyDynamic: PropDynamicRo, "fragment": { Type: schema.TypeBool, Optional: true, Description: "Matches fragmented packets. First (starting) fragment does not count. If connection tracking " + "is enabled there will be no fragments as system automatically assembles every packet", }, "hotspot": { Type: schema.TypeString, Optional: true, Description: "Matches packets received from HotSpot clients against various HotSpot matchers.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"auth", "from-client", "http", "local-dst", "to-client"}, false, true), }, // https://help.mikrotik.com/docs/display/ROS/L3+Hardware+Offloading#L3HardwareOffloading-RoutingFilters // Firewall filter rules have hw-offload option for Fasttrack, allowing fine-tuning connection offloading. "hw_offload": { Type: schema.TypeBool, Optional: true, Description: "Connection offloading for Fasttrack.", }, "icmp_options": { Type: schema.TypeString, Optional: true, Description: "Matches ICMP type: code fields.", }, "in_bridge_port": { Type: schema.TypeString, Optional: true, Description: "Actual interface the packet has entered the router if the incoming interface is a bridge. " + "Works only if use-ip-firewall is enabled in bridge settings.", }, "in_bridge_port_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as in-bridge-port.", }, "in_interface": { Type: schema.TypeString, Optional: true, Description: "Interface the packet has entered the router.", }, "in_interface_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as in-interface.", }, "ingress_priority": { Type: schema.TypeInt, Optional: true, Description: "Matches the priority of an ingress packet. Priority may be derived from VLAN, WMM, DSCP, " + "or MPLS EXP bit.", ValidateFunc: validation.IntBetween(0, 63), }, "invalid": { Type: schema.TypeBool, Computed: true, }, "ipsec_policy": { Type: schema.TypeString, Optional: true, Description: "Matches the policy used by IPsec. Value is written in the following format: direction, policy.", ValidateFunc: validation.StringMatch(regexp.MustCompile(`^(in|out)\s?,\s?(ipsec|none)$`), "Value must be written in the following format: direction, policy."), }, "ipv4_options": { Type: schema.TypeString, Optional: true, Description: "Matches IPv4 header options.", ValidateFunc: validation.StringInSlice([]string{ "any", "loose-source-routing", "no-record-route", "no-router-alert", "no-source-routing", "no-timestamp", "none", "record-route", "router-alert", "strict-source-routing", "timestamp", }, false), }, "jump_target": { Type: schema.TypeString, Optional: true, Description: "Name of the target chain to jump to. Applicable only if action=jump.", }, "layer7_protocol": { Type: schema.TypeString, Optional: true, Description: "Layer7 filter name.", }, "limit": { Type: schema.TypeString, Optional: true, Description: "Matches packets up to a limited rate (packet rate or bit rate). A rule using this matcher " + "will match until this limit is reached. Parameters are written in the following format: " + "rate[/time],burst:mode.", }, "log": { Type: schema.TypeBool, Optional: true, Description: "Add a message to the system log.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "log_prefix": { Type: schema.TypeString, Optional: true, Description: "Adds specified text at the beginning of every log message. Applicable if action=log or " + "log=yes configured.", }, "nth": { Type: schema.TypeString, Optional: true, Description: "Matches every nth packet: nth=2,1 rule will match every first packet of 2, hence, 50% of " + "all the traffic that is matched by the rule", }, "out_bridge_port": { Type: schema.TypeString, Optional: true, Description: "Actual interface the packet is leaving the router if the outgoing interface is a bridge. " + "Works only if use-ip-firewall is enabled in bridge settings.", }, "out_bridge_port_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as out-bridge-port.", }, "out_interface": { Type: schema.TypeString, Optional: true, Description: "Interface the packet is leaving the router.", }, "out_interface_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as out-interface.", }, "packet_mark": { Type: schema.TypeString, Optional: true, Description: "Matches packets marked via mangle facility with particular packet mark. If no-mark is set, " + "the rule will match any unmarked packet.", }, "packet_size": { Type: schema.TypeString, Optional: true, Description: "Matches packets of specified size or size range in bytes.", }, "per_connection_classifier": { Type: schema.TypeString, Optional: true, Description: "PCC matcher allows dividing traffic into equal streams with the ability to keep packets " + "with a specific set of options in one particular stream.", }, KeyPlaceBefore: PropPlaceBefore, "port": { Type: schema.TypeString, Optional: true, Description: "Matches if any (source or destination) port matches the specified list of ports or port " + "ranges. Applicable only if protocol is TCP or UDP", }, "priority": { Type: schema.TypeInt, Optional: true, Description: "Matches the packet's priority after a new priority has been set. Priority may be derived from " + "VLAN, WMM, DSCP, MPLS EXP bit, or from the priority that has been set using the set-priority action.", ValidateFunc: validation.IntBetween(0, 63), }, "protocol": { Type: schema.TypeString, Optional: true, Description: "Matches particular IP protocol specified by protocol name or number.", }, "psd": { Type: schema.TypeString, Optional: true, Description: "Attempts to detect TCP and UDP scans. Parameters are in the following format WeightThreshold, " + "DelayThreshold, LowPortWeight, HighPortWeight.", }, "random": { Type: schema.TypeInt, Optional: true, Description: "Matches packets randomly with a given probability.", ValidateFunc: validation.IntBetween(1, 99), }, "reject_with": { Type: schema.TypeString, Optional: true, Description: "Specifies ICMP error to be sent back if the packet is rejected. Applicable if action=reject.", ValidateFunc: validation.StringInSlice([]string{ "icmp-admin-prohibited", "icmp-net-prohibited", "icmp-protocol-unreachable", "icmp-host-prohibited", "icmp-network-unreachable", "tcp-reset", "icmp-host-unreachable", "icmp-port-unreachable", }, false), }, "routing_table": { Type: schema.TypeString, Optional: true, Description: "Matches packets which destination address is resolved in specific a routing table.", }, "routing_mark": { Type: schema.TypeString, Optional: true, Description: "Matches packets marked by mangle facility with particular routing mark.", }, "src_address": { Type: schema.TypeString, Optional: true, Description: "Matches packets which source is equal to specified IP or falls into a specified IP range.", DiffSuppressFunc: ImplicitSingleHostCIDR4, }, "src_address_list": { Type: schema.TypeString, Optional: true, Description: "Matches source address of a packet against user-defined address list.", }, "src_address_type": { Type: schema.TypeString, Optional: true, Description: "Matches source address type.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"unicast", "local", "broadcast", "multicast"}, false, true), }, "src_port": { Type: schema.TypeString, Optional: true, Description: "List of source ports and ranges of source ports. Applicable only if a protocol is TCP or UDP.", }, "src_mac_address": { Type: schema.TypeString, Optional: true, Description: "Matches source MAC address of the packet.", ValidateFunc: ValidationMacAddress, }, "tcp_flags": { Type: schema.TypeString, Optional: true, Description: "Matches specified TCP flags.", }, "tcp_mss": { Type: schema.TypeString, Optional: true, Description: "Matches TCP MSS value of an IP packet.", }, "time": { Type: schema.TypeString, Optional: true, Description: "Allows to create a filter based on the packets' arrival time and date or, for locally " + "generated packets, departure time and date.", }, "tls_host": { Type: schema.TypeString, Optional: true, Description: "Allows matching HTTPS traffic based on TLS SNI hostname.", }, "ttl": { Type: schema.TypeString, Optional: true, Description: "Matches packets TTL value.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { skip := resSchema[MetaSkipFields].Default.(string) resSchema[MetaSkipFields].Default = skip + `,"place_before"` defer func() { resSchema[MetaSkipFields].Default = skip }() return ResourceUpdate(ctx, resSchema, d, m) }, DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_firewall_filter_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIPFirewallFilterAddress = "routeros_firewall_filter.rule" func TestAccIPFirewallFilterTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/firewall/filter", "routeros_firewall_filter"), Steps: []resource.TestStep{ { Config: testAccIPFirewallFilterConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIPFirewallFilterAddress), resource.TestCheckResourceAttr(testIPFirewallFilterAddress, "action", "accept"), ), }, }, }) }) } } func testAccIPFirewallFilterConfig() string { return providerConfig + ` resource "routeros_firewall_filter" "rule" { action = "accept" chain = "forward" src_address = "10.0.0.1" dst_address = "10.0.1.1" dst_port = "443" protocol = "tcp" } resource "routeros_ip_firewall_filter" "testepeg" { action = "add-dst-to-address-list" address_list_timeout = "00:00:10" protocol = "tcp" tls_host = "globo" address_list = "teste" chain = "forward" src_address_list = "LAN" } ` } ================================================ FILE: routeros/resource_ip_firewall_layer7_protocol.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*1", "name": "rdp", "regexp": "rdpdr.*cliprdr.*rdpsnd" } */ // https://help.mikrotik.com/docs/display/ROS/ func ResourceIpFirewallLayer7Protocol() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/firewall/layer7-protocol"), MetaId: PropId(Id), "name": { Type: schema.TypeString, Optional: true, Description: "Descriptive name of l7 pattern used by configuration in firewall rules.", }, "regexp": { Type: schema.TypeString, Optional: true, Description: "POSIX compliant regular expression is used to match a pattern.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_firewall_layer7_protocol_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpFirewallLayer7Protocol = "routeros_ip_firewall_layer7_protocol.test" func TestAccIpFirewallLayer7ProtocolTest_basic(t *testing.T) { t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/firewall/layer7-protocol", "routeros_ip_firewall_layer7_protocol"), Steps: []resource.TestStep{ { Config: testAccIpFirewallLayer7ProtocolConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpFirewallLayer7Protocol), resource.TestCheckResourceAttr(testIpFirewallLayer7Protocol, "name", "rdp"), resource.TestCheckResourceAttr(testIpFirewallLayer7Protocol, "regexp", "rdpdr.*cliprdr.*rdpsnd"), ), }, }, }) }) } } func testAccIpFirewallLayer7ProtocolConfig() string { return fmt.Sprintf(`%v resource "routeros_ip_firewall_layer7_protocol" "test" { name = "rdp" regexp = "rdpdr.*cliprdr.*rdpsnd" } `, providerConfig) } ================================================ FILE: routeros/resource_ip_firewall_mangle.go ================================================ package routeros import ( "context" "regexp" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "action": "accept", "bytes": "31611", "chain": "prerouting", "disabled": "false", "dynamic": "false", "invalid": "false", "log": "false", "log-prefix": "", "packets": "325" } */ // ResourceIPFirewallMangle https://wiki.mikrotik.com/wiki/Manual:IP/Firewall/Mangle func ResourceIPFirewallMangle() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/firewall/mangle"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("bytes", "packets"), MetaSetUnsetFields: PropSetUnsetFields("dst_address", "dst_address_list", "src_address", "src_address_list", "in_interface", "in_interface_list", "out_interface", "out_interface_list", "in_bridge_port_list", "out_bridge_port_list"), "action": { Type: schema.TypeString, Required: true, Description: "Action to take if a packet is matched by the rule.", ValidateFunc: validation.StringInSlice([]string{ "accept", "add-dst-to-address-list", "add-src-to-address-list", "change-dscp", "change-mss", "change-ttl", "clear-df", "fasttrack-connection", "jump", "log", "mark-connection", "mark-packet", "mark-routing", "passthrough", "return", "route", "set-priority", "sniff-pc", "sniff-tzsp", "strip-ipv4-options", }, false), }, "address_list": { Type: schema.TypeString, Optional: true, Description: "Name of the address list to be used. Applicable if action is add-dst-to-address-list or " + "add-src-to-address-list.", }, "address_list_timeout": { Type: schema.TypeString, Optional: true, Default: "none-dynamic", Description: "Time interval after which the address will be removed from the address list specified by " + "address-list parameter. Used in conjunction with add-dst-to-address-list or add-src-to-address-list " + "actions.", }, "chain": { Type: schema.TypeString, Required: true, Description: "Specifies to which chain rule will be added. If the input does not match the name of an " + "already defined chain, a new chain will be created.", }, KeyComment: PropCommentRw, "connection_bytes": { Type: schema.TypeString, Optional: true, Description: "Matches packets only if a given amount of bytes has been transfered through the particular " + "connection.", }, "connection_limit": { Type: schema.TypeString, Optional: true, Description: "Matches connections per address or address block after given value is reached. Should be " + "used together with connection-state=new and/or with tcp-flags=syn because matcher is very resource " + "intensive.", }, "connection_mark": { Type: schema.TypeString, Optional: true, Description: "Matches packets marked via mangle facility with particular connection mark. If no-mark is " + "set, rule will match any unmarked connection.", }, "connection_nat_state": { Type: schema.TypeString, Optional: true, Description: "Can match connections that are srcnatted, dstnatted or both.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"srcnat", "dstnat"}, false, true), }, // See comment for the "path_cost" field in resource_interface_bridge_port.go file. "connection_rate": { Type: schema.TypeString, Optional: true, Description: "Connection Rate is a firewall matcher that allow to capture traffic based on present speed " + "of the connection (0..4294967295).", }, "connection_state": { Type: schema.TypeString, Optional: true, Description: "Interprets the connection tracking analysis data for a particular packet.", ValidateDiagFunc: ValidationMultiValInSlice([]string{ "established", "invalid", "new", "related", "untracked", }, false, true), }, "connection_type": { Type: schema.TypeString, Optional: true, Description: "Matches packets from related connections based on information from their connection " + "tracking helpers.", ValidateDiagFunc: ValidationMultiValInSlice([]string{ "ftp", "h323", "irc", "pptp", "quake3", "sip", "tftp", }, false, true), }, "content": { Type: schema.TypeString, Optional: true, Description: "Match packets that contain specified text.", }, KeyDisabled: PropDisabledRw, "dscp": { Type: schema.TypeInt, Optional: true, Description: "Matches DSCP IP header field.", ValidateFunc: validation.IntBetween(0, 63), }, "dst_address": { Type: schema.TypeString, Optional: true, Description: "Matches packets which destination is equal to specified IP or falls into specified IP range.", DiffSuppressFunc: ImplicitSingleHostCIDR4, }, "dst_address_list": { Type: schema.TypeString, Optional: true, Description: "Matches destination address of a packet against user-defined address list.", }, "dst_address_type": { Type: schema.TypeString, Optional: true, Description: "Matches destination address type.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"unicast", "local", "broadcast", "multicast", "blackhole"}, false, true), }, "dst_limit": { Type: schema.TypeString, Optional: true, Description: "Matches packets until a given rate is exceeded.", }, "dst_port": { Type: schema.TypeString, Optional: true, Description: "List of destination port numbers or port number ranges.", }, KeyDynamic: PropDynamicRo, "fragment": { Type: schema.TypeBool, Optional: true, Description: "Matches fragmented packets. First (starting) fragment does not count. If connection tracking " + "is enabled there will be no fragments as system automatically assembles every packet", }, "hotspot": { Type: schema.TypeString, Optional: true, Description: "Matches packets received from HotSpot clients against various HotSpot matchers.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"auth", "from-client", "http", "local-dst", "to-client"}, false, true), }, "icmp_options": { Type: schema.TypeString, Optional: true, Description: "Matches ICMP type: code fields.", }, "in_bridge_port": { Type: schema.TypeString, Optional: true, Description: "Actual interface the packet has entered the router if the incoming interface is a bridge. " + "Works only if use-ip-firewall is enabled in bridge settings.", }, "in_bridge_port_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as in-bridge-port.", }, "in_interface": { Type: schema.TypeString, Optional: true, Description: "Interface the packet has entered the router.", }, "in_interface_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as in-interface.", }, "ingress_priority": { Type: schema.TypeInt, Optional: true, Description: "Matches the priority of an ingress packet. Priority may be derived from VLAN, WMM, DSCP, " + "or MPLS EXP bit.", ValidateFunc: validation.IntBetween(0, 63), }, "invalid": { Type: schema.TypeBool, Computed: true, }, "ipsec_policy": { Type: schema.TypeString, Optional: true, Description: "Matches the policy used by IPsec. Value is written in the following format: direction, policy.", ValidateFunc: validation.StringMatch(regexp.MustCompile(`^(in|out)\s?,\s?(ipsec|none)$`), "Value must be written in the following format: direction, policy."), }, "ipv4_options": { Type: schema.TypeString, Optional: true, Description: "Matches IPv4 header options.", ValidateFunc: validation.StringInSlice([]string{ "any", "loose-source-routing", "no-record-route", "no-router-alert", "no-source-routing", "no-timestamp", "none", "record-route", "router-alert", "strict-source-routing", "timestamp", }, false), }, "jump_target": { Type: schema.TypeString, Optional: true, Description: "Name of the target chain to jump to. Applicable only if action=jump.", }, "layer7_protocol": { Type: schema.TypeString, Optional: true, Description: "Layer7 filter name.", }, "limit": { Type: schema.TypeString, Optional: true, Description: "Matches packets up to a limited rate (packet rate or bit rate). A rule using this matcher " + "will match until this limit is reached. Parameters are written in the following format: " + "rate[/time],burst:mode.", }, "log": { Type: schema.TypeBool, Optional: true, Default: false, Description: "Add a message to the system log.", }, "log_prefix": { Type: schema.TypeString, Optional: true, Description: "Adds specified text at the beginning of every log message. Applicable if action=log or " + "log=yes configured.", }, "new_connection_mark": { Type: schema.TypeString, Optional: true, Description: "Sets a new connection-mark value.", }, "new_dscp": { Type: schema.TypeInt, Optional: true, Description: "Sets a new DSCP value for a packet.", ValidateFunc: validation.IntBetween(0, 63), }, "new_mss": { Type: schema.TypeString, Optional: true, Description: "Sets a new MSS for a packet.\n * clamp-to-pmtu feature sets (DF) bit in the IP header to " + "dynamically discover the PMTU of a path.\n * Host sends all datagrams on that path with the DF bit set " + "until receives ICMP.\n * Destination Unreachable messages with a code meaning `fragmentation needed and " + "DF set`.\n * Upon receipt of such a message, the source host reduces its assumed PMTU for the path.", ValidateFunc: validation.StringMatch(regexp.MustCompile(`^(\d+|clamp-to-pmtu)$`), `Value must be a number in quotes or the string "clamp-to-pmtu".`), }, "new_packet_mark": { Type: schema.TypeString, Optional: true, Description: "Sets a new packet-mark value.", }, "new_priority": { Type: schema.TypeString, Optional: true, Description: "Sets a new priority for a packet. This can be the VLAN, WMM, DSCP or MPLS EXP priority. " + "This property can also be used to set an internal priority.", }, "new_routing_mark": { Type: schema.TypeString, Optional: true, Description: "Sets a new routing-mark value.", }, "new_ttl": { Type: schema.TypeString, Optional: true, Description: "Sets a new TTL for a packet.", }, "nth": { Type: schema.TypeString, Optional: true, Description: "Matches every nth packet: nth=2,1 rule will match every first packet of 2, hence, 50% of " + "all the traffic that is matched by the rule", }, "out_bridge_port": { Type: schema.TypeString, Optional: true, Description: "Actual interface the packet is leaving the router if the outgoing interface is a bridge. " + "Works only if use-ip-firewall is enabled in bridge settings.", }, "out_bridge_port_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as out-bridge-port.", }, "out_interface": { Type: schema.TypeString, Optional: true, Description: "Interface the packet is leaving the router.", }, "out_interface_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as out-interface.", }, "packet_mark": { Type: schema.TypeString, Optional: true, Description: "Matches packets marked via mangle facility with particular packet mark. If no-mark is set, " + "the rule will match any unmarked packet.", }, "packet_size": { Type: schema.TypeString, Optional: true, Description: "Matches packets of specified size or size range in bytes.", }, "passthrough": { Type: schema.TypeBool, Optional: true, Computed: true, Description: "Whether to let the packet to pass further (like action passthrough) into the " + "firewall or not (property only valid some actions).", }, "per_connection_classifier": { Type: schema.TypeString, Optional: true, Description: "PCC matcher allows dividing traffic into equal streams with the ability to keep packets " + "with a specific set of options in one particular stream.", }, KeyPlaceBefore: PropPlaceBefore, "port": { Type: schema.TypeString, Optional: true, Description: "Matches if any (source or destination) port matches the specified list of ports or port " + "ranges. Applicable only if protocol is TCP or UDP", }, "protocol": { Type: schema.TypeString, Optional: true, Description: "Matches particular IP protocol specified by protocol name or number.", }, "psd": { Type: schema.TypeString, Optional: true, Description: "Attempts to detect TCP and UDP scans. Parameters are in the following format WeightThreshold, " + "DelayThreshold, LowPortWeight, HighPortWeight.", }, "random": { Type: schema.TypeInt, Optional: true, Description: "Matches packets randomly with a given probability.", ValidateFunc: validation.IntBetween(1, 99), }, "routing_mark": { Type: schema.TypeString, Optional: true, Description: "Matches packets marked by mangle facility with particular routing mark.", }, "route_dst": { Type: schema.TypeString, Optional: true, Description: "Matches packets with a specific gateway.", ValidateFunc: validation.IsIPAddress, }, "src_address": { Type: schema.TypeString, Optional: true, Description: "Matches packets which source is equal to specified IP or falls into a specified IP range.", }, "src_address_list": { Type: schema.TypeString, Optional: true, Description: "Matches source address of a packet against user-defined address list.", }, "src_address_type": { Type: schema.TypeString, Optional: true, Description: "Matches source address type.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"unicast", "local", "broadcast", "multicast"}, false, true), }, "src_port": { Type: schema.TypeString, Optional: true, Description: "List of source ports and ranges of source ports. Applicable only if a protocol is TCP or UDP.", }, "src_mac_address": { Type: schema.TypeString, Optional: true, Description: "Matches source MAC address of the packet.", ValidateFunc: validation.IsMACAddress, }, "tcp_flags": { Type: schema.TypeString, Optional: true, Description: "Matches specified TCP flags.", }, "tcp_mss": { Type: schema.TypeString, Optional: true, Description: "Matches TCP MSS value of an IP packet.", }, "time": { Type: schema.TypeString, Optional: true, Description: "Allows to create a filter based on the packets' arrival time and date or, for locally " + "generated packets, departure time and date.", }, "tls_host": { Type: schema.TypeString, Optional: true, Description: "Allows matching HTTPS traffic based on TLS SNI hostname.", }, "ttl": { Type: schema.TypeString, Optional: true, Description: "Matches packets TTL value.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { skip := resSchema[MetaSkipFields].Default.(string) resSchema[MetaSkipFields].Default = skip + `,"place_before"` defer func() { resSchema[MetaSkipFields].Default = skip }() return ResourceUpdate(ctx, resSchema, d, m) }, DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_firewall_mangle_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIPFirewallMangle = "routeros_firewall_mangle.data" func TestAccIPFirewallMangleTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/firewall/mangle", "routeros_firewall_mangle"), Steps: []resource.TestStep{ { Config: testAccIPFirewallMangleConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIPFirewallMangle), resource.TestCheckResourceAttr(testIPFirewallMangle, "chain", "prerouting"), resource.TestCheckResourceAttr(testIPFirewallMangle, "action", "mark-connection"), resource.TestCheckResourceAttr(testIPFirewallMangle, "new_connection_mark", "test-mark"), ), }, }, }) }) } } func testAccIPFirewallMangleConfig() string { return providerConfig + ` resource "routeros_firewall_mangle" "data" { chain = "prerouting" action = "mark-connection" new_connection_mark = "test-mark" comment = "new-mangle-rule" } ` } ================================================ FILE: routeros/resource_ip_firewall_nat.go ================================================ package routeros import ( "context" "regexp" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*2", "action": "masquerade", "bytes": "0", "chain": "srcnat", "disabled": "false", "dynamic": "false", "invalid": "false", "log": "false", "log-prefix": "", "out-interface": "all-wireless", "packets": "0", "src-address-list": "LAN" } */ // ResourceIPFirewallNat https://help.mikrotik.com/docs/display/ROS/NAT func ResourceIPFirewallNat() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/firewall/nat"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("bytes", "packets"), MetaSetUnsetFields: PropSetUnsetFields("dst_address", "dst_address_list", "src_address", "src_address_list", "in_interface", "in_interface_list", "out_interface", "out_interface_list", "in_bridge_port_list", "out_bridge_port_list"), "action": { Type: schema.TypeString, Required: true, Description: "Action to take if a packet is matched by the rule", ValidateFunc: validation.StringInSlice([]string{ "accept", "add-dst-to-address-list", "add-src-to-address-list", "dst-nat", "endpoint-independent-nat", "jump", "log", "masquerade", "netmap", "passthrough", "redirect", "return", "same", "src-nat", }, false), }, "address_list": { Type: schema.TypeString, Optional: true, Description: "Name of the address list to be used. Applicable if action is add-dst-to-address-list or " + "add-src-to-address-list.", }, "address_list_timeout": { Type: schema.TypeString, Optional: true, Description: "Time interval after which the address will be removed from the address list specified by " + "address-list parameter. Used in conjunction with add-dst-to-address-list or add-src-to-address-list " + "actions.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "chain": { Type: schema.TypeString, Required: true, Description: "Specifies to which chain rule will be added. If the input does not match the name of an " + "already defined chain, a new chain will be created.", }, KeyComment: PropCommentRw, "connection_bytes": { Type: schema.TypeString, Optional: true, Description: "Matches packets only if a given amount of bytes has been transfered through the particular " + "connection.", }, "connection_limit": { Type: schema.TypeString, Optional: true, Description: "Matches connections per address or address block after given value is reached. Should be " + "used together with connection-state=new and/or with tcp-flags=syn because matcher is very resource " + "intensive.", }, "connection_mark": { Type: schema.TypeString, Optional: true, Description: "Matches packets marked via mangle facility with particular connection mark. If no-mark is " + "set, rule will match any unmarked connection.", }, // See comment for the "path_cost" field in resource_interface_bridge_port.go file. "connection_rate": { Type: schema.TypeString, Optional: true, Description: "Connection Rate is a firewall matcher that allow to capture traffic based on present speed " + "of the connection (0..4294967295).", }, "connection_type": { Type: schema.TypeString, Optional: true, Description: "Matches packets from related connections based on information from their connection " + "tracking helpers.", ValidateDiagFunc: ValidationMultiValInSlice([]string{ "ftp", "h323", "irc", "pptp", "quake3", "sip", "tftp", }, false, true), }, "content": { Type: schema.TypeString, Optional: true, Description: "Match packets that contain specified text.", }, KeyDisabled: PropDisabledRw, "dscp": { Type: schema.TypeInt, Optional: true, Description: "Matches DSCP IP header field.", ValidateFunc: validation.IntBetween(0, 63), }, "dst_address": { Type: schema.TypeString, Optional: true, Description: "Matches packets which destination is equal to specified IP or falls into specified IP range.", DiffSuppressFunc: ImplicitSingleHostCIDR4, }, "dst_address_list": { Type: schema.TypeString, Optional: true, Description: "Matches destination address of a packet against user-defined address list.", }, "dst_address_type": { Type: schema.TypeString, Optional: true, Description: "Matches destination address type.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"unicast", "local", "broadcast", "multicast"}, false, true), }, "dst_limit": { Type: schema.TypeString, Optional: true, Description: "Matches packets until a given rate is exceeded.", }, "dst_port": { Type: schema.TypeString, Optional: true, Description: "List of destination port numbers or port number ranges.", }, KeyDynamic: PropDynamicRo, "fragment": { Type: schema.TypeBool, Optional: true, Description: "Matches fragmented packets. First (starting) fragment does not count. If connection tracking " + "is enabled there will be no fragments as system automatically assembles every packet", }, "hotspot": { Type: schema.TypeString, Optional: true, Description: "Matches packets received from HotSpot clients against various HotSpot matchers.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"auth", "from-client", "http", "local-dst", "to-client"}, false, true), }, "icmp_options": { Type: schema.TypeString, Optional: true, Description: "Matches ICMP type: code fields.", }, "in_bridge_port": { Type: schema.TypeString, Optional: true, Description: "Actual interface the packet has entered the router if the incoming interface is a bridge. " + "Works only if use-ip-firewall is enabled in bridge settings.", }, "in_bridge_port_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as in-bridge-port.", }, "in_interface": { Type: schema.TypeString, Optional: true, Description: "Interface the packet has entered the router.", }, "in_interface_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as in-interface.", }, "ingress_priority": { Type: schema.TypeInt, Optional: true, Description: "Matches the priority of an ingress packet. Priority may be derived from VLAN, WMM, DSCP, " + "or MPLS EXP bit.", ValidateFunc: validation.IntBetween(0, 63), }, "invalid": { Type: schema.TypeBool, Computed: true, }, "ipsec_policy": { Type: schema.TypeString, Optional: true, Description: "Matches the policy used by IPsec. Value is written in the following format: direction, policy.", ValidateFunc: validation.StringMatch(regexp.MustCompile(`^(in|out)\s?,\s?(ipsec|none)$`), "Value must be written in the following format: direction, policy."), }, "ipv4_options": { Type: schema.TypeString, Optional: true, Description: "Matches IPv4 header options.", ValidateFunc: validation.StringInSlice([]string{ "any", "loose-source-routing", "no-record-route", "no-router-alert", "no-source-routing", "no-timestamp", "none", "record-route", "router-alert", "strict-source-routing", "timestamp", }, false), }, "jump_target": { Type: schema.TypeString, Optional: true, Description: "Name of the target chain to jump to. Applicable only if action=jump.", }, "layer7_protocol": { Type: schema.TypeString, Optional: true, Description: "Layer7 filter name.", }, "limit": { Type: schema.TypeString, Optional: true, Description: "Matches packets up to a limited rate (packet rate or bit rate). A rule using this matcher " + "will match until this limit is reached. Parameters are written in the following format: " + "rate[/time],burst:mode.", }, "log": { Type: schema.TypeBool, Optional: true, Description: "Add a message to the system log.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "log_prefix": { Type: schema.TypeString, Optional: true, Description: "Adds specified text at the beginning of every log message. Applicable if action=log or " + "log=yes configured.", }, "nth": { Type: schema.TypeString, Optional: true, Description: "Matches every nth packet: nth=2,1 rule will match every first packet of 2, hence, 50% of " + "all the traffic that is matched by the rule", }, "out_bridge_port": { Type: schema.TypeString, Optional: true, Description: "Actual interface the packet is leaving the router if the outgoing interface is a bridge. " + "Works only if use-ip-firewall is enabled in bridge settings.", }, "out_bridge_port_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as out-bridge-port.", }, "out_interface": { Type: schema.TypeString, Optional: true, Description: "Interface the packet is leaving the router.", }, "out_interface_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as out-interface.", }, "packet_mark": { Type: schema.TypeString, Optional: true, Description: "Matches packets marked via mangle facility with particular packet mark. If no-mark is set, " + "the rule will match any unmarked packet.", }, "packet_size": { Type: schema.TypeString, Optional: true, Description: "Matches packets of specified size or size range in bytes.", }, "per_connection_classifier": { Type: schema.TypeString, Optional: true, Description: "PCC matcher allows dividing traffic into equal streams with the ability to keep packets " + "with a specific set of options in one particular stream.", }, KeyPlaceBefore: PropPlaceBefore, "port": { Type: schema.TypeString, Optional: true, Description: "Matches if any (source or destination) port matches the specified list of ports or port " + "ranges. Applicable only if protocol is TCP or UDP", }, "priority": { Type: schema.TypeInt, Optional: true, Description: "Matches the packet's priority after a new priority has been set. Priority may be derived from " + "VLAN, WMM, DSCP, MPLS EXP bit, or from the priority that has been set using the set-priority action.", ValidateFunc: validation.IntBetween(0, 63), }, "protocol": { Type: schema.TypeString, Optional: true, Description: "Matches particular IP protocol specified by protocol name or number.", }, "psd": { Type: schema.TypeString, Optional: true, Description: "Attempts to detect TCP and UDP scans. Parameters are in the following format WeightThreshold, " + "DelayThreshold, LowPortWeight, HighPortWeight.", }, "random": { Type: schema.TypeInt, Optional: true, Description: "Matches packets randomly with a given probability.", ValidateFunc: validation.IntBetween(1, 99), }, "randomise_ports": { Type: schema.TypeBool, Optional: true, Description: "Randomize to which public port connections will be mapped.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "routing_mark": { Type: schema.TypeString, Optional: true, Description: "Matches packets marked by mangle facility with particular routing mark.", }, "same_not_by_dst": { Type: schema.TypeBool, Optional: true, Description: "Specifies whether to take into account or not destination IP address when selecting a " + "new source IP address. Applicable if action=same", }, "src_address": { Type: schema.TypeString, Optional: true, Description: "Matches packets which source is equal to specified IP or falls into a specified IP range.", DiffSuppressFunc: ImplicitSingleHostCIDR4, }, "src_address_list": { Type: schema.TypeString, Optional: true, Description: "Matches source address of a packet against user-defined address list.", }, "src_address_type": { Type: schema.TypeString, Optional: true, Description: "Matches source address type.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"unicast", "local", "broadcast", "multicast"}, false, true), }, "src_port": { Type: schema.TypeString, Optional: true, Description: "List of source ports and ranges of source ports. Applicable only if a protocol is TCP or UDP.", }, "src_mac_address": { Type: schema.TypeString, Optional: true, Description: "Matches source MAC address of the packet.", ValidateFunc: validation.IsMACAddress, }, // https://help.mikrotik.com/docs/spaces/ROS/pages/343244851/Socksify "socks5_port": { Type: schema.TypeInt, Optional: true, Description: "Listening port of the SOCKS5 proxy server.", ValidateFunc: validation.IsPortNumber, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "socks5_server": { Type: schema.TypeString, Optional: true, Description: "IP address of the SOCKS5 proxy server. (only IPv4 addresses are supported)", ValidateFunc: validation.IsIPv4Address, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "socksify_service": { Type: schema.TypeString, Optional: true, Description: "Name of existing socksify service.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "tcp_mss": { Type: schema.TypeString, Optional: true, Description: "Matches TCP MSS value of an IP packet.", }, "time": { Type: schema.TypeString, Optional: true, Description: "Allows to create a filter based on the packets' arrival time and date or, for locally " + "generated packets, departure time and date.", }, "to_addresses": { Type: schema.TypeString, Optional: true, Description: "Replace original address with specified one. Applicable if action is dst-nat, netmap, " + "same, src-nat.", }, "to_ports": { Type: schema.TypeString, Optional: true, Description: "Replace the original port with the specified one. Applicable if action is dst-nat, " + "redirect, masquerade, netmap, same, src-nat.", }, "ttl": { Type: schema.TypeString, Optional: true, Description: "Matches packets TTL value.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { skip := resSchema[MetaSkipFields].Default.(string) resSchema[MetaSkipFields].Default = skip + `,"place_before"` defer func() { resSchema[MetaSkipFields].Default = skip }() return ResourceUpdate(ctx, resSchema, d, m) }, DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_firewall_nat_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIPFirewallNat = "routeros_firewall_nat.data" func TestAccIPFirewallNatTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/firewall/nat", "routeros_firewall_nat"), Steps: []resource.TestStep{ { Config: testAccIPFirewallNatConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIPFirewallNat), resource.TestCheckResourceAttr(testIPFirewallNat, "chain", "srcnat"), resource.TestCheckResourceAttr(testIPFirewallNat, "action", "masquerade"), resource.TestCheckResourceAttr(testIPFirewallNat, "disabled", "true"), ), }, }, }) }) } } func testAccIPFirewallNatConfig() string { return providerConfig + ` resource "routeros_firewall_nat" "data" { chain = "srcnat" action = "masquerade" disabled = true comment = "new-nat-rule" } ` } ================================================ FILE: routeros/resource_ip_firewall_raw.go ================================================ package routeros import ( "context" "regexp" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "action": "accept", "bytes": "53342", "chain": "prerouting", "comment": "1", "disabled": "false", "dynamic": "false", "invalid": "false", "log": "false", "log-prefix": "", "packets": "497" } */ // ResourceIPFirewallRaw https://wiki.mikrotik.com/wiki/Manual:IP/Firewall/Raw // https://help.mikrotik.com/docs/display/ROS/Common+Firewall+Matchers+and+Actions func ResourceIPFirewallRaw() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/firewall/raw"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("bytes", "packets"), MetaSetUnsetFields: PropSetUnsetFields("dst_address", "dst_address_list", "src_address", "src_address_list", "in_interface", "in_interface_list", "out_interface", "out_interface_list", "in_bridge_port_list", "out_bridge_port_list", "protocol"), "action": { Type: schema.TypeString, Required: true, Description: "Action to take if a packet is matched by the rule", ValidateFunc: validation.StringInSlice([]string{ "accept", "add-dst-to-address-list", "add-src-to-address-list", "drop", "jump", "log", "notrack", "passthrough", "return", }, false), }, "address_list": { Type: schema.TypeString, Optional: true, Description: "Name of the address list used in 'add-dst-to-address-list' and 'add-src-to-address-list' actions.", }, "address_list_timeout": { Type: schema.TypeString, Optional: true, Description: "Time interval after which the address will be removed from the address list specified by " + "address-list parameter. Used in conjunction with add-dst-to-address-list or add-src-to-address-list " + "actions.", DiffSuppressFunc: TimeEqual, }, "chain": { Type: schema.TypeString, Required: true, Description: "Specifies to which chain rule will be added. If the input does not match the name of an " + "already defined chain, a new chain will be created.", }, KeyComment: PropCommentRw, "content": { Type: schema.TypeString, Optional: true, Description: "Match packets that contain specified text.", }, KeyDisabled: PropDisabledRw, "dscp": { Type: schema.TypeInt, Optional: true, Description: "Matches DSCP IP header field.", ValidateFunc: validation.IntBetween(0, 63), }, "dst_address": { Type: schema.TypeString, Optional: true, Description: "Matches packets which destination is equal to specified IP or falls into specified IP range.", DiffSuppressFunc: ImplicitSingleHostCIDR4, }, "dst_address_list": { Type: schema.TypeString, Optional: true, Description: "Matches destination address of a packet against user-defined address list.", }, "dst_address_type": { Type: schema.TypeString, Optional: true, Description: "Matches destination address type.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"unicast", "local", "broadcast", "multicast"}, false, true), }, "dst_limit": { Type: schema.TypeString, Optional: true, Description: "Matches packets until a given rate is exceeded.", }, "dst_port": { Type: schema.TypeString, Optional: true, Description: "List of destination port numbers or port number ranges.", }, KeyDynamic: PropDynamicRo, "fragment": { Type: schema.TypeBool, Optional: true, Description: "Matches fragmented packets. First (starting) fragment does not count. If connection tracking " + "is enabled there will be no fragments as system automatically assembles every packet", }, "hotspot": { Type: schema.TypeString, Optional: true, Description: "Matches packets received from HotSpot clients against various HotSpot matchers.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"auth", "from-client", "http", "local-dst", "to-client"}, false, true), }, "icmp_options": { Type: schema.TypeString, Optional: true, Description: "Matches ICMP type: code fields.", }, "in_bridge_port": { Type: schema.TypeString, Optional: true, Description: "Actual interface the packet has entered the router if the incoming interface is a bridge. " + "Works only if use-ip-firewall is enabled in bridge settings.", }, "in_bridge_port_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as in-bridge-port.", }, "in_interface": { Type: schema.TypeString, Optional: true, Description: "Interface the packet has entered the router.", }, "in_interface_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as in-interface.", }, "ingress_priority": { Type: schema.TypeInt, Optional: true, Description: "Matches the priority of an ingress packet. Priority may be derived from VLAN, WMM, DSCP, " + "or MPLS EXP bit.", ValidateFunc: validation.IntBetween(0, 63), }, "invalid": { Type: schema.TypeBool, Computed: true, }, "ipsec_policy": { Type: schema.TypeString, Optional: true, Description: "Matches the policy used by IPsec. Value is written in the following format: direction, policy.", ValidateFunc: validation.StringMatch(regexp.MustCompile(`^(in|out)\s?,\s?(ipsec|none)$`), "Value must be written in the following format: direction, policy."), }, "ipv4_options": { Type: schema.TypeString, Optional: true, Description: "Matches IPv4 header options.", ValidateFunc: validation.StringInSlice([]string{ "any", "loose-source-routing", "no-record-route", "no-router-alert", "no-source-routing", "no-timestamp", "none", "record-route", "router-alert", "strict-source-routing", "timestamp", }, false), }, "jump_target": { Type: schema.TypeString, Optional: true, Description: "Name of the target chain to jump to. Applicable only if action=jump.", }, "limit": { Type: schema.TypeString, Optional: true, Description: "Matches packets up to a limited rate (packet rate or bit rate). A rule using this matcher " + "will match until this limit is reached. Parameters are written in the following format: " + "rate[/time],burst:mode.", }, "log": { Type: schema.TypeBool, Optional: true, Description: "Add a message to the system log.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "log_prefix": { Type: schema.TypeString, Optional: true, Description: "Adds specified text at the beginning of every log message. Applicable if action=log or " + "log=yes configured.", }, "nth": { Type: schema.TypeString, Optional: true, Description: "Matches every nth packet: nth=2,1 rule will match every first packet of 2, hence, 50% of " + "all the traffic that is matched by the rule", }, "out_bridge_port": { Type: schema.TypeString, Optional: true, Description: "Actual interface the packet is leaving the router if the outgoing interface is a bridge. " + "Works only if use-ip-firewall is enabled in bridge settings.", }, "out_bridge_port_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as out-bridge-port.", }, "out_interface": { Type: schema.TypeString, Optional: true, Description: "Interface the packet is leaving the router.", }, "out_interface_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as out-interface.", }, "packet_mark": { Type: schema.TypeString, Optional: true, Description: "Matches packets marked via mangle facility with particular packet mark. If no-mark is set, " + "the rule will match any unmarked packet.", }, "packet_size": { Type: schema.TypeString, Optional: true, Description: "Matches packets of specified size or size range in bytes.", }, "per_connection_classifier": { Type: schema.TypeString, Optional: true, Description: "PCC matcher allows dividing traffic into equal streams with the ability to keep packets " + "with a specific set of options in one particular stream.", }, KeyPlaceBefore: PropPlaceBefore, "port": { Type: schema.TypeString, Optional: true, Description: "Matches if any (source or destination) port matches the specified list of ports or port " + "ranges. Applicable only if protocol is TCP or UDP", }, "priority": { Type: schema.TypeInt, Optional: true, Description: "Matches the packet's priority after a new priority has been set. Priority may be derived from " + "VLAN, WMM, DSCP, MPLS EXP bit, or from the priority that has been set using the set-priority action.", ValidateFunc: validation.IntBetween(0, 63), }, "protocol": { Type: schema.TypeString, Optional: true, Description: "Matches particular IP protocol specified by protocol name or number.", }, "psd": { Type: schema.TypeString, Optional: true, Description: "Attempts to detect TCP and UDP scans. Parameters are in the following format WeightThreshold, " + "DelayThreshold, LowPortWeight, HighPortWeight.", }, "random": { Type: schema.TypeInt, Optional: true, Description: "Matches packets randomly with a given probability.", ValidateFunc: validation.IntBetween(1, 99), }, "src_address": { Type: schema.TypeString, Optional: true, Description: "Matches packets which source is equal to specified IP or falls into a specified IP range.", DiffSuppressFunc: ImplicitSingleHostCIDR4, }, "src_address_list": { Type: schema.TypeString, Optional: true, Description: "Matches source address of a packet against user-defined address list.", }, "src_address_type": { Type: schema.TypeString, Optional: true, Description: "Matches source address type.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"unicast", "local", "broadcast", "multicast", "blackhole"}, false, true), }, "src_port": { Type: schema.TypeString, Optional: true, Description: "List of source ports and ranges of source ports. Applicable only if a protocol is TCP or UDP.", }, "src_mac_address": { Type: schema.TypeString, Optional: true, Description: "Matches source MAC address of the packet.", ValidateFunc: ValidationMacAddress, }, "tcp_flags": { Type: schema.TypeString, Optional: true, Description: "Matches specified TCP flags.", }, "tcp_mss": { Type: schema.TypeString, Optional: true, Description: "Matches TCP MSS value of an IP packet.", }, "time": { Type: schema.TypeString, Optional: true, Description: "Allows to create a filter based on the packets' arrival time and date or, for locally " + "generated packets, departure time and date.", }, "tls_host": { Type: schema.TypeString, Optional: true, Description: "Allows matching HTTPS traffic based on TLS SNI hostname.", }, "ttl": { Type: schema.TypeString, Optional: true, Description: "Matches packets TTL value.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { skip := resSchema[MetaSkipFields].Default.(string) resSchema[MetaSkipFields].Default = skip + `,"place_before"` defer func() { resSchema[MetaSkipFields].Default = skip }() return ResourceUpdate(ctx, resSchema, d, m) }, DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_firewall_raw_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIPFirewallRawAddress = "routeros_ip_firewall_raw.rule" func TestAccIPFirewallRawTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/firewall/raw", "routeros_ip_firewall_raw"), Steps: []resource.TestStep{ { Config: testAccIPFirewallRawConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIPFirewallRawAddress), resource.TestCheckResourceAttr(testIPFirewallRawAddress, "action", "accept"), ), }, }, }) }) } } func testAccIPFirewallRawConfig() string { return providerConfig + ` resource "routeros_ip_firewall_raw" "rule" { action = "accept" chain = "prerouting" src_address = "10.0.0.1" dst_address = "10.0.1.1" dst_port = "443" protocol = "tcp" } ` } ================================================ FILE: routeros/resource_ip_hotspot.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*5", "HTTPS": "false", "addresses-per-mac": "unlimited", "disabled": "false", "idle-timeout": "5m", "interface": "ether4", "invalid": "false", "keepalive-timeout": "none", "login-timeout": "none", "name": "server1", "profile": "default", "proxy-status": "running" } */ // https://help.mikrotik.com/docs/pages/viewpage.action?pageId=56459266#HotSpot(Captiveportal)-IPHotSpot func ResourceIpHotspot() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/hotspot"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("HTTPS", "keepalive_timeout", "proxy_status"), "address_pool": { Type: schema.TypeString, Optional: true, Description: "Address space used to change HotSpot client any IP address to a valid address. Useful for " + "providing public network access to mobile clients that are not willing to change their networking settings.", }, "addresses_per_mac": { Type: schema.TypeString, Optional: true, Description: "Number of IP addresses allowed to be bind with the MAC address, when multiple HotSpot clients " + "connected with one MAC-address.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyDisabled: PropDisabledRw, "idle_timeout": { Type: schema.TypeString, Optional: true, Description: "Period of inactivity for unauthorized clients. When there is no traffic from this client (literally " + "client computer should be switched off), once the timeout is reached, a user is dropped from the HotSpot " + "host list, its used address becomes available.", DiffSuppressFunc: TimeEqual, }, "interface": { Type: schema.TypeString, Required: true, Description: "Interface to run HotSpot on.", }, KeyInvalid: PropInvalidRo, "keepalive_timeout": { Type: schema.TypeString, Optional: true, Description: "The exact value of the keepalive-timeout, that is applied to the user. Value shows how long " + "the host can stay out of reach to be removed from the HotSpot.", DiffSuppressFunc: TimeEqual, }, "login_timeout": { Type: schema.TypeString, Optional: true, Description: "Period of time after which if a host hasn't been authorized itself with a system the host " + "entry gets deleted from host table. Loop repeats until the host logs in the system. Enable if there " + "are situations where a host cannot log in after being too long in the host table unauthorized.", DiffSuppressFunc: TimeEqual, }, KeyName: PropName("HotSpot server's name or identifier."), "profile": { Type: schema.TypeString, Optional: true, Description: "HotSpot server default HotSpot profile, which is located in `/ip/hotspot/profile`.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_hotspot_ip_binding.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "address": "0.0.0.1", "comment": "comment", "disabled": "false", "mac-address": "00:00:00:00:01:10", "to-address": "0.0.0.2" } */ // https://help.mikrotik.com/docs/pages/viewpage.action?pageId=56459266#HotSpot(Captiveportal)-IPBinding func ResourceIpHotspotIpBinding() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/hotspot/ip-binding"), MetaId: PropId(Id), "address": { Type: schema.TypeString, Optional: true, Description: "The original IP address of the client.", }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "mac_address": { Type: schema.TypeString, Optional: true, Description: "MAC address of the client.", }, "server": { Type: schema.TypeString, Optional: true, Description: "Name of the HotSpot server. `all` - will be applied to all hotspot servers.", }, "to_address": { Type: schema.TypeString, Optional: true, Description: "New IP address of the client, translation occurs on the router (client does not know anything " + "about the translation).", }, "type": { Type: schema.TypeString, Optional: true, Description: "Type of the IP-binding action\n * regular - performs One-to-One NAT according to the rule, translates " + "the address to to-address;\n * bypassed - performs the translation, but excludes client from login to the " + "HotSpot;\n * blocked - translation is not performed and packets from a host are dropped.", ValidateFunc: validation.StringInSlice([]string{"blocked", "bypassed", "regular"}, false), }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_hotspot_ip_binding_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpHotspotIpBinding = "routeros_ip_hotspot_ip_binding.test" func TestAccIpHotspotIpBindingTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/hotspot/ip-binding", "routeros_ip_hotspot_ip_binding"), Steps: []resource.TestStep{ { Config: testAccIpHotspotIpBindingConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpHotspotIpBinding), resource.TestCheckResourceAttr(testIpHotspotIpBinding, "address", "0.0.0.1"), resource.TestCheckResourceAttr(testIpHotspotIpBinding, "comment", "comment"), resource.TestCheckResourceAttr(testIpHotspotIpBinding, "mac_address", "00:00:00:00:01:10"), resource.TestCheckResourceAttr(testIpHotspotIpBinding, "to_address", "0.0.0.2"), ), }, }, }) }) } } func testAccIpHotspotIpBindingConfig() string { return fmt.Sprintf(`%v resource "routeros_ip_hotspot_ip_binding" "test" { address = "0.0.0.1" comment = "comment" mac_address = "00:00:00:00:01:10" to_address = "0.0.0.2" } `, providerConfig) } ================================================ FILE: routeros/resource_ip_hotspot_profile.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*2", "dns-name": "", "hotspot-address": "192.168.11.1", "html-directory": "hotspot", "html-directory-override": "", "http-cookie-lifetime": "3d", "http-proxy": "0.0.0.0:0", "install-hotspot-queue": "false", "login-by": "cookie,http-chap,https", "name": "hsprof2", "smtp-server": "0.0.0.0", "split-user-domain": "false", "ssl-certificate": "tls", "use-radius": "false" } */ // https://wiki.mikrotik.com/wiki/Manual:IP/Hotspot/Profile func ResourceIpHotspotProfile() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/hotspot/profile"), MetaId: PropId(Id), "dns_name": { Type: schema.TypeString, Optional: true, Description: "DNS name of the HotSpot server (it appears as the location of the login page). This name will " + "automatically be added as a static DNS entry in the DNS cache. Name can affect if Hotspot is automatically " + "detected by client device. For example, iOS devices may not detect Hotspot that has a name which includes " + "`.local`.", }, "hotspot_address": { Type: schema.TypeString, Optional: true, Description: "IP address of HotSpot service.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "html_directory": { Type: schema.TypeString, Optional: true, Description: "Directory name in which HotSpot HTML pages are stored (by default hotspot directory). It is " + "possible to specify different directory with modified HTML pages. To change HotSpot login page, connect " + "to the router with FTP and download hotspot directory contents. v6.31 and older software builds: For " + "devices where `flash` directory is present, hotspot html directory must be stored there and path must " + "be typed in as follows: `/(hotspot_dir)`. This must be done in this order as hotspot sees `flash` " + "directory as root location. v6.32 and newer software builds: full path must be typed in html-directory " + "field, including `/flash/(hotspot_dir)`.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "html_directory_override": { Type: schema.TypeString, Optional: true, Description: "Alternative path for hotspot html files. It should be used only if customized hotspot html " + "files are stored on external storage(attached usb, hdd, etc). If configured then hotspot will switch " + "to this html path as soon at it becomes available and switch back to html-directory path if override " + "path becomes non-available for some reason.", }, "http_cookie_lifetime": { Type: schema.TypeString, Optional: true, Description: "HTTP cookie validity time, the option is related to cookie HotSpot login method.", DiffSuppressFunc: TimeEqual, }, "http_proxy": { Type: schema.TypeString, Optional: true, Description: "Address and port of the proxy server for HotSpot service, when default value is used all request " + "are resolved by the local `/ip proxy`.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "https_redirect": { Type: schema.TypeBool, Optional: true, Description: "Whether to redirect unauthenticated user to hotspot login page, if he is visiting a https:// " + "url. Since certificate domain name will mismatch, often this leads to errors, so you can set this parameter " + "to `no` and all https requests will simply be rejected and user will have to visit a http page.", }, "login_by": { Type: schema.TypeSet, Optional: true, Description: "Used HotSpot authentication method\n " + "* mac-cookie - enables login by mac cookie method.\n " + "* cookie - may only be used with other HTTP authentication method. HTTP cookie is generated, when user authenticates " + "in HotSpot for the first time. User is not asked for the login/password and authenticated automatically, " + "until cookie-lifetime is active.\n " + "* http-chap - login/password is required for the user to authenticate in HotSpot. CHAP " + "challenge-response method with MD5 hashing algorithm is used for protecting passwords. \n " + "* http-pap - login/password is required for user to authenticate in HotSpot. Username and password are " + "sent over network in plain text.\n " + "* https - login/password is required for user to authenticate in HotSpot. Client login/password " + "exchange between client and server is encrypted with SSL tunnel.\n " + "* mac - client is authenticated without asking login form. Client MAC-address is added to `/ip hotspot " + "user` database, client is authenticated as soon as connected to the HotSpot\n " + "* trial - client is allowed to use internet without HotSpot login for the specified amount of time.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"cookie", "http-chap", "http-pap", "https", "mac", "trial", "mac-cookie"}, false), }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "mac_auth_mode": { Type: schema.TypeString, Optional: true, Description: "Allows to control User-Name and User-Password RADIUS attributes when using MAC authentication.", ValidateFunc: validation.StringInSlice([]string{"mac-as-username", "mac-as-username-and-password"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "mac_auth_password": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "Used together with MAC authentication, field used to specify password for the users to be " + "authenticated by their MAC addresses. The following option is required, when specific RADIUS server " + "rejects authentication for the clients with blank password.", }, KeyName: PropName("Descriptive name of the profile."), "nas_port_type": { Type: schema.TypeString, Optional: true, Description: "`NAS-Port-Type` value to be sent to RADIUS server, `NAS-Port-Type` values are described in the " + "RADIUS RFC 2865. This optional value attribute indicates the type of the physical port of the HotSpot " + "server.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "radius_accounting": { Type: schema.TypeBool, Optional: true, Description: "Send RADIUS server accounting information for each user, when yes is used.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "radius_default_domain": { Type: schema.TypeString, Optional: true, Description: "Default domain to use for RADIUS requests. Allows to use separate RADIUS server per `/ip hotspot " + "profile`. If used, same domain name should be specified under `/radius domain` value.", }, "radius_interim_update": { Type: schema.TypeString, Optional: true, Description: "How often to send accounting updates . When received is set, interim-time is used from RADIUS " + "server. 0s is the same as received.", DiffSuppressFunc: TimeEqual, }, "radius_location_id": { Type: schema.TypeString, Optional: true, Description: "`RADIUS-Location-Id` property.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "radius_location_name": { Type: schema.TypeString, Optional: true, Description: "`RADIUS-Location-Id` to be sent to RADIUS server. Used to identify location of the HotSpot server " + "during the communication with RADIUS server. Value is optional and used together with RADIUS server.", }, "radius_mac_format": { Type: schema.TypeString, Optional: true, Description: "Controls how the MAC address of the client is encoded in the `User-Name` and `User-Password` " + "attributes when using MAC authentication.", ValidateFunc: validation.StringInSlice([]string{"XX XX XX XX XX XX", "XX:XX:XX:XX:XX:XX", "XXXXXX-XXXXXX", "XXXXXXXXXXXX", "XX-XX-XX-XX-XX-XX", "XXXX:XXXX:XXXX", "XXXXXX:XXXXXX"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "rate_limit": { Type: schema.TypeString, Optional: true, Description: "Rate limitation in form of rx-rate[/tx-rate] [rx-burst-rate[/tx-burst-rate] [rx-burst-threshold[/tx-burst-threshold] " + "[rx-burst-time[/tx-burst-time]]]] [priority] [rx-rate-min[/tx-rate-min]] from the point of view of the " + "router (so `rx` is client upload, and `tx` is client download). All rates should be numbers with " + "optional 'k' (1,000s) or 'M' (1,000,000s). If tx-rate is not specified, rx-rate is as tx-rate too. Same " + "goes for tx-burst-rate and tx-burst-threshold and tx-burst-time. If both rx-burst-threshold and tx-burst-threshold " + "are not specified (but burst-rate is specified), rx-rate and tx-rate is used as burst thresholds. If " + "both rx-burst-time and tx-burst-time are not specified, 1s is used as default. rx-rate-min and tx-rate " + "min are the values of limit-at properties.", }, "smtp_server": { Type: schema.TypeString, Optional: true, Description: "SMTP server address to be used to redirect HotSpot users SMTP requests.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "split_user_domain": { Type: schema.TypeBool, Optional: true, Description: "Split username from domain name when the username is given in `user@domain` or in `domain\\user` " + "format from RADIUS server.", }, "ssl_certificate": { Type: schema.TypeString, Optional: true, Description: "Name of the SSL certificate on the router to to use only for HTTPS authentication.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "trial_uptime_limit": { Type: schema.TypeString, Optional: true, Description: "Used only with trial authentication method. Time value specifies, how long trial user " + "identified by MAC address can use access to public networks without HotSpot authentication.", DiffSuppressFunc: TimeEqual, }, "trial_uptime_reset": { Type: schema.TypeString, Optional: true, Description: "Used only with trial authentication method.", DiffSuppressFunc: TimeEqual, }, "trial_user_profile": { Type: schema.TypeString, Optional: true, Description: "Specifies hotspot user profile for trial users.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "use_radius": { Type: schema.TypeBool, Optional: true, Description: "Use RADIUS to authenticate HotSpot users.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_hotspot_profile_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpHotspotProfile = "routeros_ip_hotspot_profile.test" func TestAccIpHotspotProfileTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/hotspot/profile", "routeros_ip_hotspot_profile"), Steps: []resource.TestStep{ { Config: testAccIpHotspotProfileConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpHotspotProfile), resource.TestCheckResourceAttr(testIpHotspotProfile, "name", "hsprof-1"), resource.TestCheckResourceAttr(testIpHotspotProfile, "login_by.0", "https"), resource.TestCheckResourceAttr(testIpHotspotProfile, "login_by.1", "mac"), resource.TestCheckResourceAttr(testIpHotspotProfile, "login_by.2", "trial"), resource.TestCheckResourceAttr(testIpHotspotProfile, "use_radius", "true"), ), }, }, }) }) } } func testAccIpHotspotProfileConfig() string { return fmt.Sprintf(`%v resource "routeros_ip_hotspot_profile" "test" { name = "hsprof-1" login_by = ["mac", "https", "trial"] use_radius = true } `, providerConfig) } ================================================ FILE: routeros/resource_ip_hotspot_service_port.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*1", "disabled": "false", "name": "ftp", "ports": "21" } */ func ResourceIpHotspotServicePort() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/hotspot/service-port"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("name"), KeyDisabled: PropDisabledRw, KeyName: PropName("Service name."), "ports": { Type: schema.TypeString, Computed: true, }, } return &schema.Resource{ CreateContext: DefaultCreateUpdate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultCreateUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_hotspot_service_port_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpHotspotServicePort = "routeros_ip_hotspot_service_port.test" func TestAccIpHotspotServicePortTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccIpHotspotServicePortConfig("true"), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpHotspotServicePort), resource.TestCheckResourceAttr(testIpHotspotServicePort, "disabled", "true"), resource.TestCheckResourceAttr(testIpHotspotServicePort, "name", "ftp"), ), }, { Config: testAccIpHotspotServicePortConfig("false"), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpHotspotServicePort), resource.TestCheckResourceAttr(testIpHotspotServicePort, "disabled", "false"), resource.TestCheckResourceAttr(testIpHotspotServicePort, "name", "ftp"), ), }, }, }) }) } } func testAccIpHotspotServicePortConfig(param string) string { return fmt.Sprintf(`%v resource "routeros_ip_hotspot_service_port" "test" { name = "ftp" disabled = %v } `, providerConfig, param) } ================================================ FILE: routeros/resource_ip_hotspot_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpHotspot = "routeros_ip_hotspot.test" func TestAccIpHotspotTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/hotspot", "routeros_ip_hotspot"), Steps: []resource.TestStep{ { Config: testAccIpHotspotConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpHotspot), resource.TestCheckResourceAttr(testIpHotspot, "name", "server-1"), resource.TestCheckResourceAttr(testIpHotspot, "interface", "ether3"), ), }, }, }) }) } } func testAccIpHotspotConfig() string { return fmt.Sprintf(`%v resource "routeros_ip_hotspot" "test" { name = "server-1" interface = "ether3" } `, providerConfig) } ================================================ FILE: routeros/resource_ip_hotspot_user.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*1", "address": "0.0.0.1", "bytes-in": "0", "bytes-out": "0", "disabled": "false", "dynamic": "false", "email": "mail@g.com", "limit-bytes-in": "100", "limit-bytes-out": "200", "limit-bytes-total": "500", "limit-uptime": "1m", "mac-address": "11:00:00:00:00:00", "name": "user1", "packets-in": "0", "packets-out": "0", "password": "123", "profile": "default", "routes": "10.0.0.0/24", "uptime": "0s" } */ // https://wiki.mikrotik.com/wiki/Manual:IP/Hotspot/User#Users func ResourceIpHotspotUser() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/hotspot/user"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("bytes_in", "bytes_out", "packets_in", "packets_out", "uptime"), "address": { Type: schema.TypeInt, Optional: true, Description: "IP address, when specified client will get the address from the HotSpot one-to-one NAT translations. " + "Address does not restrict HotSpot login only from this address.", }, KeyComment: PropCommentRw, KeyDefault: PropDefaultRo, KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, "email": { Type: schema.TypeString, Optional: true, Description: "HotSpot client's e-mail, informational value for the HotSpot user.", }, "limit_bytes_in": { Type: schema.TypeInt, Optional: true, Description: "Maximal amount of bytes that can be received from the user. User is disconnected from HotSpot " + "after the limit is reached.", }, "limit_bytes_out": { Type: schema.TypeInt, Optional: true, Description: "Maximal amount of bytes that can be transmitted from the user. User is disconnected from HotSpot " + "after the limit is reached.", }, "limit_bytes_total": { Type: schema.TypeInt, Optional: true, Description: "(limit-bytes-in+limit-bytes-out). User is disconnected from HotSpot after the limit is reached.", }, "limit_uptime": { Type: schema.TypeInt, Optional: true, Description: "Uptime limit for the HotSpot client, user is disconnected from HotSpot as soon as uptime is " + "reached.", }, "mac_address": { Type: schema.TypeInt, Optional: true, Description: "Client is allowed to login only from the specified MAC-address. If value is 00:00:00:00:00:00, " + "any mac address is allowed.", }, KeyName: PropName("HotSpot login page username, when MAC-address authentication is used name is configured as " + "client's MAC-address."), "password": { Type: schema.TypeString, Optional: true, Description: "User password.", Sensitive: true, }, "profile": { Type: schema.TypeString, Optional: true, Description: "User profile configured in `/ip hotspot user profile`.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "routes": { Type: schema.TypeString, Optional: true, Description: "Routes added to HotSpot gateway when client is connected. The route format dst-address gateway " + "metric (for example, `192.168.1.0/24 192.168.0.1 1`).", }, "server": { Type: schema.TypeString, Optional: true, Description: "HotSpot server's name to which user is allowed login.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_hotspot_user_profile.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "add-mac-cookie": "true", "address-list": "aaa", "advertise": "true", "advertise-interval": "30m,10m", "advertise-timeout": "immediately", "advertise-url": "https://www.mikrotik.com/", "idle-timeout": "none", "incoming-filter": "bbb", "incoming-packet-mark": "ddd", "insert-queue-before": "first", "keepalive-timeout": "2m", "mac-cookie-timeout": "3d", "name": "uprof1", "on-login": "s1", "on-logout": "s2", "open-status-page": "always", "outgoing-filter": "ccc", "outgoing-packet-mark": "eee", "parent-queue": "none", "queue-type": "default-small", "rate-limit": "1", "session-timeout": "1s", "shared-users": "1", "status-autorefresh": "1m", "transparent-proxy": "true" } */ // https://wiki.mikrotik.com/wiki/Manual:IP/Hotspot/User#User_Profile func ResourceIpHotspotUserProfile() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/hotspot/user/profile"), MetaId: PropId(Id), MetaSetUnsetFields: PropSetUnsetFields("insert_queue_before", "parent_queue", "queue_type"), "add_mac_cookie": { Type: schema.TypeBool, Optional: true, Description: "Allows to add mac cookie for users.", }, "address_list": { Type: schema.TypeString, Optional: true, Description: "Name of the address list in which users IP address will be added. Useful to mark traffic per " + "user groups for queue tree configurations.", }, "address_pool": { Type: schema.TypeString, Optional: true, Description: "IP pool name from which the user will get IP. When user has improper network settings configuration " + "on the computer, HotSpot server makes translation and assigns correct IP address from the pool instead " + "of incorrect one.", }, "advertise": { Type: schema.TypeBool, Optional: true, Description: "Enable forced advertisement popups. After certain interval specific web-page is being displayed " + "for HotSpot users. Advertisement page might be blocked by browsers popup blockers.", }, "advertise_interval": { Type: schema.TypeSet, Optional: true, Description: "Set of intervals between advertisement popups. After the list is done, the last value is used " + "for all further advertisements, 10 minutes.", Elem: &schema.Schema{ Type: schema.TypeString, DiffSuppressFunc: TimeEqual, }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "advertise_timeout": { Type: schema.TypeString, Optional: true, Description: "How long advertisement is shown, before blocking network access for HotSpot client. Connection " + "to Internet is not allowed, when advertisement is not shown.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "advertise_url": { Type: schema.TypeString, Optional: true, Description: "List of URLs that is show for advertisement popups. After the last URL is used, list starts " + "from the begining.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyDefault: PropDefaultRo, "idle_timeout": { Type: schema.TypeString, Optional: true, Description: "Maximal period of inactivity for authorized HotSpot clients. Timer is counting, when there " + "is no traffic coming from that client and going through the router, for example computer is switched " + "off. User is logged out, dropped of the host list, the address used by the user is freed, when timeout " + "is reached.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "incoming_filter": { Type: schema.TypeString, Optional: true, Description: "Name of the firewall chain applied to incoming packets from the users of this profile, jump " + "rule is required from built-in chain (input, forward, output) to chain=hotspot.", }, "incoming_packet_mark": { Type: schema.TypeString, Optional: true, Description: "Packet mark put on incoming packets from every user of this profile.", }, "insert_queue_before": { Type: schema.TypeString, Optional: true, Description: "", ValidateFunc: validation.StringInSlice([]string{"first", "bottom"}, false), }, "keepalive_timeout": { Type: schema.TypeString, Optional: true, Description: "Keepalive timeout for authorized HotSpot clients. Used to detect, that the computer of the " + "client is alive and reachable. User is logged out, when timeout value is reached.", DiffSuppressFunc: TimeEqual, }, "mac_cookie_timeout": { Type: schema.TypeString, Optional: true, Description: "Selects mac-cookie timeout from last login or logout. Read more>>.", DiffSuppressFunc: TimeEqual, }, KeyName: PropName("Descriptive name of the profile."), "on_login": { Type: schema.TypeString, Optional: true, Description: "Script name to be executed, when user logs in to the HotSpot from the particular profile. " + "It is possible to get username from internal user and interface variable. For example, :log info ``User " + "$user logged in!`` . If hotspot is set on bridge interface, then interface variable will show bridge " + "as actual interface unless use-ip-firewall' is set in bridge settings. List of available variables: " + "$user $username (alternative var name for $user) $address $``mac-address`` $interface.", }, "on_logout": { Type: schema.TypeString, Optional: true, Description: "Script name to be executed, when user logs out from the HotSpot.It is possible to get username " + "from internal user and interface variable. For example, :log info ``User $user logged in!`` . If hotspot " + "is set on bridge interface, then interface variable will show bridge as actual interface unless use-ip-firewall " + "is set in bridge settings. List of available variables: $user $username (alternative var name for $user) " + "$address $``mac-address`` $interface $cause Starting with v6.34rc11 some additional variables are available: " + "$uptime-secs - final session time in seconds $bytes-in - bytes uploaded $bytes-out - bytes downloaded " + "$bytes-total - bytes up + bytes down $packets-in - packets uploaded $packets-out - packets downloaded " + "$packets-total - packets up + packets down.", }, "open_status_page": { Type: schema.TypeString, Optional: true, Description: "Option to show status page for user authenticated with mac login method. For example to show " + "advertisement on status page (alogin.html) http-login - open status page only for HTTP login (includes " + "cookie and HTTPS) always - open HTTP status page in case of mac login as well.", ValidateFunc: validation.StringInSlice([]string{"always", "http-login"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "outgoing_filter": { Type: schema.TypeString, Optional: true, Description: "Name of the firewall chain applied to outgoing packets from the users of this profile, jump " + "rule is required from built-in chain (input, forward, output) to chain=hotspot.", }, "outgoing_packet_mark": { Type: schema.TypeString, Optional: true, Description: "Packet mark put on outgoing packets from every user of this profile.", }, "parent_queue": { Type: schema.TypeString, Optional: true, Description: "", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "queue_type": { Type: schema.TypeString, Optional: true, Description: "", ValidateFunc: validation.StringInSlice([]string{"default", "default-small", "ethernet-default", "hotspot-default", "multi-queue-ethernet-default", "only-hardware-queue", "pcq-download-default", "pcq-upload-default", "synchronous-default", "wireless-default"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "rate_limit": { Type: schema.TypeString, Optional: true, Description: "Simple dynamic queue is created for user, once it logs in to the HotSpot. Rate-limitation " + "is configured in the following form [rx-rate[/tx-rate] [rx-burst-rate[/tx-burst-rate] [rx-burst-threshold[/tx-burst-threshold] " + "[rx-burst-time[/tx-burst-time] [priority] [rx-rate-min[/tx-rate-min]]]]. For example, to set 1M download, " + "512k upload for the client, rate-limit=512k/1M.", }, "session_timeout": { Type: schema.TypeString, Optional: true, Description: "Allowed session time for client. After this time, the user is logged out unconditionally.", DiffSuppressFunc: TimeEqual, }, "shared_users": { Type: schema.TypeString, // Maybe "unlimited". Optional: true, Description: "Allowed number of simultaneously logged in users with the same HotSpot username.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "status_autorefresh": { Type: schema.TypeString, Optional: true, Description: "HotSpot status page autorefresh interval.", DiffSuppressFunc: TimeEqual, }, "transparent_proxy": { Type: schema.TypeBool, Optional: true, Description: "Use transparent HTTP proxy for the authorized users of this profile.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_hotspot_user_profile_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpHotspotUserProfile = "routeros_ip_hotspot_user_profile.test" func TestAccIpHotspotUserProfileTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/hotspot/user/profile", "routeros_ip_hotspot_user_profile"), Steps: []resource.TestStep{ { Config: testAccIpHotspotUserProfileConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpHotspotUserProfile), resource.TestCheckResourceAttr(testIpHotspotUserProfile, "advertise", "true"), ), }, }, }) }) } } func testAccIpHotspotUserProfileConfig() string { return fmt.Sprintf(`%v resource "routeros_ip_hotspot_user_profile" "test" { add_mac_cookie = true address_list = "list-1" idle_timeout = "none" keepalive_timeout = "2m" mac_cookie_timeout = "3d" name = "new-profile" shared_users = 3 status_autorefresh = "2m" transparent_proxy = true advertise = true } `, providerConfig) } ================================================ FILE: routeros/resource_ip_hotspot_user_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpHotspotUser = "routeros_ip_hotspot_user.test" func TestAccIpHotspotUserTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/hotspot/user", "routeros_ip_hotspot_user"), Steps: []resource.TestStep{ { Config: testAccIpHotspotUserConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpHotspotUser), resource.TestCheckResourceAttr(testIpHotspotUser, "name", "user-1"), resource.TestCheckResourceAttr(testIpHotspotUser, "profile", "default"), ), }, }, }) }) } } func testAccIpHotspotUserConfig() string { return fmt.Sprintf(`%v resource "routeros_ip_hotspot_user" "test" { name = "user-1" } `, providerConfig) } ================================================ FILE: routeros/resource_ip_hotspot_walled_garden.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*6", "action": "deny", "disabled": "false", "dst-host": "1.2.3.4", "dst-port": "!123", "dynamic": "false", "hits": "0", "method": "GET", "path": "/sss", "server": "server1", "src-address": "4.3.2.1" } */ // https://wiki.mikrotik.com/wiki/Manual:IP/Hotspot/Walled_Garden func ResourceIpHotspotWalledGarden() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/hotspot/walled-garden"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("hits", "dst_address"), "action": { Type: schema.TypeString, Optional: true, Description: "Action to perform, when packet matches the rule `allow` - allow access to the web-page without " + "authorization, `deny` - the authorization is required to access the web-page.", ValidateFunc: validation.StringInSlice([]string{"allow", "deny"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "dst_host": { Type: schema.TypeString, Optional: true, Description: "Domain name of the destination web-server.", }, "dst_port": { Type: schema.TypeString, Optional: true, Description: "TCP port number, client sends request to.", }, KeyDynamic: PropDynamicRo, "method": { Type: schema.TypeString, Optional: true, Description: "HTTP method of the request.", }, "path": { Type: schema.TypeString, Optional: true, Description: "The path of the request, path comes after `http://dst_host/`.", }, "server": { Type: schema.TypeString, Optional: true, Description: "Name of the HotSpot server, rule is applied to.", }, "src_address": { Type: schema.TypeString, Optional: true, Description: "Source address of the user, usually IP address of the HotSpot client.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_hotspot_walled_garden_ip.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*4", "action": "reject", "disabled": "false", "dst-address": "!0.0.0.0", "dst-address-list": "bbb", "dst-port": "0-65535", "invalid": "false", "protocol": "tcp", "server": "server1", "src-address": "0.0.0.0", "src-address-list": "aaa" } */ // https://wiki.mikrotik.com/wiki/Manual:IP/Hotspot/Walled_Garden#IP_Walled_Garden func ResourceIpHotspotWalledGardenIp() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/hotspot/walled-garden/ip"), MetaId: PropId(Id), "action": { Type: schema.TypeString, Optional: true, Description: "Action to perform, when packet matches the rule allow - allow access to the web-page without " + "authorization deny - the authorization is required to access the web-page reject - the authorization " + "is required to access the resource, ICMP reject message will be sent to client, when packet will match " + "the rule.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "dst_address": { Type: schema.TypeString, Optional: true, Description: "Destination IP address, IP address of the WEB-server. Ignored if dst-host is already specified.", ConflictsWith: []string{"dst_host"}, }, "dst_address_list": { Type: schema.TypeString, Optional: true, Description: "Destination IP address list. Ignored if dst-host is already specified.", }, "dst_host": { Type: schema.TypeString, Optional: true, Description: "Domain name of the destination web-server. When this parameter is specified dynamic entry " + "is added to Walled Garden.", ConflictsWith: []string{"dst_address"}, }, "dst_port": { Type: schema.TypeString, Optional: true, Description: "TCP port number, client sends request to.", }, KeyInvalid: PropInvalidRo, "protocol": { Type: schema.TypeString, Optional: true, Description: "IP protocol.", }, "server": { Type: schema.TypeString, Optional: true, Description: "Name of the HotSpot server, rule is applied to.", }, "src_address": { Type: schema.TypeString, Optional: true, Description: "Source address of the user, usually IP address of the HotSpot client.", }, "src_address_list": { Type: schema.TypeString, Optional: true, Description: "Source IP address list.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_hotspot_walled_garden_ip_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpHotspotWalledGardenIp = "routeros_ip_hotspot_walled_garden_ip.test" func TestAccIpHotspotWalledGardenIpTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/hotspot/walled-garden/ip", "routeros_ip_hotspot_walled_garden_ip"), Steps: []resource.TestStep{ { Config: testAccIpHotspotWalledGardenIpConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpHotspotWalledGardenIp), resource.TestCheckResourceAttr(testIpHotspotWalledGardenIp, "action", "reject"), resource.TestCheckResourceAttr(testIpHotspotWalledGardenIp, "dst_address", "!0.0.0.0"), resource.TestCheckResourceAttr(testIpHotspotWalledGardenIp, "dst_address_list", "dlist"), resource.TestCheckResourceAttr(testIpHotspotWalledGardenIp, "dst_port", "0-65535"), resource.TestCheckResourceAttr(testIpHotspotWalledGardenIp, "protocol", "tcp"), resource.TestCheckResourceAttr(testIpHotspotWalledGardenIp, "src_address", "0.0.0.0"), resource.TestCheckResourceAttr(testIpHotspotWalledGardenIp, "src_address_list", "slist"), ), }, }, }) }) } } func testAccIpHotspotWalledGardenIpConfig() string { return fmt.Sprintf(`%v resource "routeros_ip_hotspot_walled_garden_ip" "test" { action = "reject" dst_address = "!0.0.0.0" dst_address_list = "dlist" dst_port = "0-65535" protocol = "tcp" src_address = "0.0.0.0" src_address_list = "slist" } `, providerConfig) } ================================================ FILE: routeros/resource_ip_hotspot_walled_garden_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpHotspotWalledGarden = "routeros_ip_hotspot_walled_garden.test" func TestAccIpHotspotWalledGardenTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/hotspot/walled-garden", "routeros_ip_hotspot_walled_garden"), Steps: []resource.TestStep{ { Config: testAccIpHotspotWalledGardenConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpHotspotWalledGarden), resource.TestCheckResourceAttr(testIpHotspotWalledGarden, "action", "deny"), resource.TestCheckResourceAttr(testIpHotspotWalledGarden, "dst_host", "1.2.3.4"), resource.TestCheckResourceAttr(testIpHotspotWalledGarden, "dst_port", "!443"), ), }, }, }) }) } } func testAccIpHotspotWalledGardenConfig() string { return fmt.Sprintf(`%v resource "routeros_ip_hotspot_walled_garden" "test" { action = "deny" dst_host = "1.2.3.4" dst_port = "!443" } `, providerConfig) } ================================================ FILE: routeros/resource_ip_ipsec_identity.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*A", "auth-method": "pre-shared-key", "disabled": "false", "dynamic": "false", "generate-policy": "no", "peer": "peer1", "secret": "secret!!!" } */ // https://help.mikrotik.com/docs/display/ROS/IPsec#IPsec-Identities func ResourceIpIpsecIdentity() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/ipsec/identity"), MetaId: PropId(Id), "auth_method": { Type: schema.TypeString, Optional: true, Description: "Authentication method:\n * digital-signature - authenticate using a pair of RSA certificates;\n * eap " + "- IKEv2 EAP authentication for initiator (peer with a netmask of `/32`). Must be used together with eap-methods;\n * eap-radius " + "- IKEv2 EAP RADIUS passthrough authentication for the responder (RFC 3579). A server certificate in " + "this case is required. If a server certificate is not specified then only clients supporting EAP-only " + "(RFC 5998) will be able to connect. Note that the EAP method should be compatible with EAP-only;\n * pre-shared-key " + "- authenticate by a password (pre-shared secret) string shared between the peers (not recommended since " + "an offline attack on the pre-shared key is possible);\n * rsa-key - authenticate using an RSA key imported " + "in keys menu. Only supported in IKEv1;\n * pre-shared-key-xauth - authenticate by a password (pre-shared " + "secret) string shared between the peers + XAuth username and password. Only supported in IKEv1;\n * rsa-signature-hybrid " + "- responder certificate authentication with initiator XAuth. Only supported in IKEv1.", ValidateFunc: validation.StringInSlice([]string{"digital-signature", "eap", "eap-radius", "pre-shared-key", "pre-shared-key-xauth", "rsa-key", "rsa-signature-hybrid"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "certificate": { Type: schema.TypeString, Optional: true, Description: "Name of a certificate listed in System/Certificates (signing packets; the certificate must " + "have the private key). Applicable if digital signature authentication method (`auth-method=digital-signature`) " + "or EAP (a`uth-method=eap`) is used.", }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, "eap_methods": { Type: schema.TypeString, Optional: true, Description: "All EAP methods requires whole certificate chain including intermediate and root CA certificates " + "to be present in System/Certificates menu. Also, the username and password (if required by the authentication " + "server) must be specified. Multiple EAP methods may be specified and will be used in a specified order. " + "Currently supported EAP methods:\n * eap-mschapv2;\n * eap-peap - also known as PEAPv0/EAP-MSCHAPv2;\n * eap-tls - " + "requires additional client certificate specified under certificate parameter;\n * eap-ttls.", ValidateFunc: validation.StringInSlice([]string{"eap-mschapv2", "eap-peap", "eap-tls", "eap-ttls"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "generate_policy": { Type: schema.TypeString, Optional: true, Description: "Allow this peer to establish SA for non-existing policies. Such policies are created dynamically " + "for the lifetime of SA. Automatic policies allows, for example, to create IPsec secured L2TP tunnels, " + "or any other setup where remote peer's IP address is not known at the configuration time. `no` - do not " + "generate policies; `port-override` - generate policies and force policy to use any port (old behavior); " + "`port-strict` - use ports from peer's proposal, which should match peer's policy.", ValidateFunc: validation.StringInSlice([]string{"no", "port-override", "port-strict"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "key": { Type: schema.TypeString, Optional: true, Description: "Name of the private key from keys menu. Applicable if RSA key authentication method (`auth-method=rsa-key`) " + "is used.", }, "match_by": { Type: schema.TypeString, Optional: true, Description: "Defines the logic used for peer's identity validation. `remote-id` - will verify the peer's ID " + "according to remote-id setting. `certificate` will verify the peer's certificate with what is specified " + "under remote-certificate setting.", ValidateFunc: validation.StringInSlice([]string{"remote-id", "certificate"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "mode_config": { Type: schema.TypeString, Optional: true, Description: "Name of the configuration parameters from mode-config menu. When parameter is set mode-config " + "is enabled.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "my_id": { Type: schema.TypeString, Optional: true, Description: "On initiator, this controls what ID_i is sent to the responder. On responder, this controls " + "what ID_r is sent to the initiator. In IKEv2, responder also expects this ID in received ID_r from initiator. `auto` " + "- tries to use correct ID automatically: IP for pre-shared key, SAN (DN if not present) for certificate " + "based connections; `address` - IP address is used as ID;dn - the binary Distinguished Encoding Rules (DER) " + "encoding of an ASN.1 X.500 Distinguished Name; `fqdn` - fully qualified domain name; `key-id` - use the specified " + "key ID for the identity; `user-fqdn` - specifies a fully-qualified username string, for example, `user@domain.com`.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "notrack_chain": { Type: schema.TypeString, Optional: true, Description: "Adds IP/Firewall/Raw rules matching IPsec policy to a specified chain. Use together with generate-policy.", }, "password": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "XAuth or EAP password. Applicable if pre-shared key with XAuth authentication method " + "(`auth-method=pre-shared-key-xauth`) or EAP (`auth-method=eap`) is used.", }, "peer": { Type: schema.TypeString, Required: true, Description: "Name of the peer on which the identity applies.", }, "policy_template_group": { Type: schema.TypeString, Optional: true, Description: "If generate-policy is enabled, traffic selectors are checked against templates from the same " + "group. If none of the templates match, Phase 2 SA will not be established.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "remote_certificate": { Type: schema.TypeString, Optional: true, Description: "Name of a certificate (listed in `System/Certificates`) for authenticating the remote side (validating " + "packets; no private key required). If a remote-certificate is not specified then the received certificate " + "from a remote peer is used and checked against CA in the certificate menu. Proper CA must be imported " + "in a certificate store. If remote-certificate and match-by=certificate is specified, only the specific " + "client certificate will be matched. Applicable if digital signature authentication method " + "(`auth-method=digital-signature`) is used.", }, "remote_id": { Type: schema.TypeString, Optional: true, Description: "This parameter controls what ID value to expect from the remote peer. Note that all types " + "except for ignoring will verify remote peer's ID with a received certificate. In case when the peer " + "sends the certificate name as its ID, it is checked against the certificate, else the ID is checked " + "against Subject Alt. Name. `auto` - accept all ID's;address - IP address is used as ID;dn - the binary " + "Distinguished Encoding Rules (DER) encoding of an ASN.1 X.500 Distinguished Name; `fqdn` - fully qualified " + "domain name. Only supported in IKEv2; `user-fqdn` - a fully-qualified username string, for example, `user@domain.com`. " + "Only supported in IKEv2; `key-id` - specific key ID for the identity. Only supported in IKEv2; `ignore` - " + "do not verify received ID with certificate (dangerous). * Wildcard key ID matching **is not supported**, " + "for example `remote-id=`key-id:CN=*.domain.com`.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "remote_key": { Type: schema.TypeString, Optional: true, Description: "Name of the public key from keys menu. Applicable if RSA key authentication method " + "(`auth-method=rsa-key`) is used.", }, "secret": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "Secret string. If it starts with '0x', it is parsed as a hexadecimal value. Applicable if " + "pre-shared key authentication method (`auth-method=pre-shared-key` and `auth-method=pre-shared-key-xauth`) " + "is used.", }, "username": { Type: schema.TypeString, Optional: true, Description: "XAuth or EAP username. Applicable if pre-shared key with XAuth authentication method " + "(`auth-method=pre-shared-key-xauth`) or EAP (`auth-method=eap`) is used.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_ipsec_identity_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" ) const testIpIpsecIdentity = "routeros_ip_ipsec_identity.identity" func TestAccIpIpsecIdentityTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/ipsec/identity", "routeros_ip_ipsec_identity"), Steps: []resource.TestStep{ { Config: testAccIpIpsecIdentityConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpIpsecIdentity), resource.TestCheckResourceAttr(testIpIpsecIdentity, "auth_method", "eap"), resource.TestCheckResourceAttr(testIpIpsecIdentity, "certificate", ""), resource.TestCheckResourceAttr(testIpIpsecIdentity, "eap_methods", "eap-mschapv2"), resource.TestCheckResourceAttr(testIpIpsecIdentity, "generate_policy", "port-strict"), resource.TestCheckResourceAttr(testIpIpsecIdentity, "mode_config", "NordVPN-i"), resource.TestCheckResourceAttr(testIpIpsecIdentity, "peer", "NordVPN-i"), resource.TestCheckResourceAttr(testIpIpsecIdentity, "username", "support@mikrotik.com"), resource.TestCheckResourceAttr(testIpIpsecIdentity, "password", "secret"), ), }, { Config: testAccIpIpsecIdentityConfig(), ResourceName: testIpIpsecIdentity, ImportStateId: `peer=NordVPN-i`, ImportState: true, ImportStateCheck: func(states []*terraform.InstanceState) error { if len(states) != 1 { return fmt.Errorf("more than 1 states received, only one expected") } return nil }, }, }, }) }) } } func testAccIpIpsecIdentityConfig() string { return fmt.Sprintf(`%v resource "routeros_ip_ipsec_mode_config" "mode-for-identity" { name = "NordVPN-i" responder = false } resource "routeros_ip_ipsec_peer" "peer-for-identity" { address = "lv30.nordvpn.com" exchange_mode = "ike2" name = "NordVPN-i" } resource "routeros_ip_ipsec_identity" "identity" { auth_method = "eap" certificate = "" eap_methods = "eap-mschapv2" generate_policy = "port-strict" mode_config = routeros_ip_ipsec_mode_config.mode-for-identity.name peer = routeros_ip_ipsec_peer.peer-for-identity.name username = "support@mikrotik.com" password = "secret" } `, providerConfig) } ================================================ FILE: routeros/resource_ip_ipsec_key.go ================================================ package routeros import ( "context" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "key-size": "1024", <<<< !!! /ip/ipsec/key/generate-key name=new-key key-size= 2048 4096 8192 "name": "new-key", "private-key": "true", "rsa": "true" } */ /* 14.10.2025 In version 7.20, RSA key generation has been moved to a separate submenu /ip/ipsec/key/rsa. I think MT plans to add support for keys other than RSA. So for now, we will leave this resource unchanged to avoid making another fix that breaks the logic of the program. */ // https://help.mikrotik.com/docs/display/ROS/IPsec#IPsec-Keys func ResourceIpIpsecKey() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/ipsec/key"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("private_key", "rsa"), "key_size": { Type: schema.TypeInt, Required: true, ForceNew: true, Description: "Size of this key.", ValidateFunc: validation.IntInSlice([]int{1024, 2048, 4096}), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyName: PropName(""), } return &schema.Resource{ CreateContext: func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { return ResourceCreateAndWait(ctxSetCrudMethod(ctx, crudGenerateKey), resSchema, d, m, d.Timeout(schema.TimeoutCreate)) }, ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_ipsec_key_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpIpsecKey = "routeros_ip_ipsec_key.test" func TestAccIpIpsecKeyTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/ipsec/key", "routeros_ip_ipsec_key"), Steps: []resource.TestStep{ { Config: testAccIpIpsecKeyConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpIpsecKey), resource.TestCheckResourceAttr(testIpIpsecKey, "name", "test-key"), resource.TestCheckResourceAttr(testIpIpsecKey, "key_size", "2048"), ), }, }, }) }) } } func testAccIpIpsecKeyConfig() string { return fmt.Sprintf(`%v resource "routeros_ip_ipsec_key" "test" { name = "test-key" key_size = 2048 } `, providerConfig) } ================================================ FILE: routeros/resource_ip_ipsec_mode_config.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*3", "address-pool": "default-dhcp", "address-prefix-length": "24", "name": "cfg1", "responder": "true", "split-dns": "1.1.1.1", "split-include": "0.0.0.0/0", "system-dns": "true" } */ // https://help.mikrotik.com/docs/display/ROS/IPsec#IPsec-Modeconfigs func ResourceIpIpsecModeConfig() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/ipsec/mode-config"), MetaId: PropId(Id), "address": { Type: schema.TypeString, Optional: true, Description: "Single IP address for the initiator instead of specifying a whole address pool.", ValidateFunc: validation.IsIPv4Address, ConflictsWith: []string{"address_pool"}, }, "address_pool": { Type: schema.TypeString, Optional: true, Description: "Name of the address pool from which the responder will try to assign address if mode-config " + "is enabled.", ConflictsWith: []string{"address"}, }, "address_prefix_length": { Type: schema.TypeInt, Optional: true, Description: "Prefix length (netmask) of the assigned address from the pool.", ValidateFunc: validation.IntBetween(1, 32), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "connection_mark": { Type: schema.TypeString, Optional: true, Description: "Firewall connection mark.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, // KeyComment: PropCommentRw, KeyName: PropName(""), "responder": { Type: schema.TypeBool, Optional: true, Description: "Specifies whether the configuration will work as an initiator (client) or responder (server). " + "The initiator will request for mode-config parameters from the responder.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "split_dns": { Type: schema.TypeSet, Optional: true, Description: "List of DNS names that will be resolved using a system-dns=yes or static-dns= setting.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.IsIPv4Address, }, }, "split_include": { Type: schema.TypeSet, Optional: true, Description: "List of subnets in CIDR format, which to tunnel. Subnets will be sent to the peer using the " + "CISCO UNITY extension, a remote peer will create specific dynamic policies.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.IsCIDR, }, }, "src_address_list": { Type: schema.TypeString, Optional: true, Description: "Specifying an address list will generate dynamic source NAT rules. This parameter is only " + "available with responder=no. A roadWarrior client with NAT.", }, "static_dns": { Type: schema.TypeString, Optional: true, Description: "Manually specified DNS server's IP address to be sent to the client.", }, "system_dns": { Type: schema.TypeBool, Optional: true, Description: "When this option is enabled DNS addresses will be taken from `/ip dns`.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "use_responder_dns": { Type: schema.TypeString, Optional: true, Description: "", ValidateFunc: validation.StringInSlice([]string{"exclusively", "yes", "no"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_ipsec_mode_config_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpIpsecModeConfig = "routeros_ip_ipsec_mode_config.test" func TestAccIpIpsecModeConfigTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/ipsec/mode-config", "routeros_ip_ipsec_mode_config"), Steps: []resource.TestStep{ { Config: testAccIpIpsecModeConfigConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpIpsecModeConfig), resource.TestCheckResourceAttr(testIpIpsecModeConfig, "name", "test-cfg"), resource.TestCheckResourceAttr(testIpIpsecModeConfig, "address", "1.2.3.4"), resource.TestCheckResourceAttr(testIpIpsecModeConfig, "split_include.0", "0.0.0.0/0"), resource.TestCheckResourceAttr(testIpIpsecModeConfig, "split_dns.0", "1.1.1.1"), ), }, }, }) }) } } func testAccIpIpsecModeConfigConfig() string { return fmt.Sprintf(`%v resource "routeros_ip_ipsec_mode_config" "test" { name = "test-cfg" address = "1.2.3.4" split_include = ["0.0.0.0/0"] split_dns = ["1.1.1.1"] } `, providerConfig) } ================================================ FILE: routeros/resource_ip_ipsec_peer.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "disabled": "false", "dynamic": "false", "exchange-mode": "main", "name": "peer1", "passive": "true", "profile": "default", "responder": "true", "send-initial-contact": "true" } */ // https://help.mikrotik.com/docs/display/ROS/IPsec#IPsec-Peers func ResourceIpIpsecPeer() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/ipsec/peer"), MetaId: PropId(Id), "address": { Type: schema.TypeString, Optional: true, Description: "If the remote peer's address matches this prefix, then the peer configuration is used in authentication " + "and establishment of Phase 1. If several peer's addresses match several configuration entries, the most " + "specific one (i.e. the one with the largest netmask) will be used.", }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, "exchange_mode": { Type: schema.TypeString, Optional: true, Description: "Different ISAKMP phase 1 exchange modes according to RFC 2408. the main mode relaxes rfc2409 " + "section 5.4, to allow pre-shared-key authentication in the main mode. ike2 mode enables Ikev2 RFC 7296. " + "Parameters that are ignored by IKEv2 proposal-check, compatibility-options, lifebytes, dpd-maximum-failures, " + "nat-traversal.", ValidateFunc: validation.StringInSlice([]string{"aggressive", "base", "main", "ike2"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "local_address": { Type: schema.TypeString, Optional: true, Description: "Routers local address on which Phase 1 should be bounded to.", }, KeyName: PropName("Peer name."), "passive": { Type: schema.TypeBool, Optional: true, Description: "When a passive mode is enabled will wait for a remote peer to initiate an IKE connection. " + "The enabled passive mode also indicates that the peer is xauth responder, and disabled passive mode " + "- xauth initiator. When a passive mode is a disabled peer will try to establish not only phase1 but " + "also phase2 automatically, if policies are configured or created during the phase1.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "port": { Type: schema.TypeInt, Optional: true, Description: "Communication port used (when a router is an initiator) to connect to remote peer in cases " + "if remote peer uses the non-default port.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "profile": { Type: schema.TypeString, Optional: true, Description: "Name of the profile template that will be used during IKE negotiation.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "responder": { Type: schema.TypeBool, Computed: true, Description: "Whether this peer will act as a responder only (listen to incoming requests) and not " + "initiate a connection.", }, "send_initial_contact": { Type: schema.TypeBool, Optional: true, Description: "Specifies whether to send `initial contact` IKE packet or wait for remote side, this packet " + "should trigger the removal of old peer SAs for current source address. Usually, in road warrior setups " + "clients are initiators and this parameter should be set to no. Initial contact is not sent if modecfg " + "or xauth is enabled for ikev1.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_ipsec_peer_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpIpsecPeer = "routeros_ip_ipsec_peer.test" func TestAccIpIpsecPeerTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/ipsec/peer", "routeros_ip_ipsec_peer"), Steps: []resource.TestStep{ { Config: testAccIpIpsecPeerConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpIpsecPeer), resource.TestCheckResourceAttr(testIpIpsecPeer, "address", "lv20.nordvpn.com"), resource.TestCheckResourceAttr(testIpIpsecPeer, "exchange_mode", "ike2"), resource.TestCheckResourceAttr(testIpIpsecPeer, "name", "NordVPN"), resource.TestCheckResourceAttr(testIpIpsecPeer, "profile", "default"), ), }, }, }) }) } } func testAccIpIpsecPeerConfig() string { return fmt.Sprintf(`%v resource "routeros_ip_ipsec_peer" "test" { address = "lv20.nordvpn.com" exchange_mode = "ike2" name = "NordVPN" } `, providerConfig) } ================================================ FILE: routeros/resource_ip_ipsec_policy.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1000001", "action": "encrypt", "active": "false", "disabled": "false", "dst-address": "::/0", "dst-port": "any", "dynamic": "false", "invalid": "false", "ipsec-protocols": "esp", "level": "require", "peer": "peer1", "ph2-count": "0", "ph2-state": "no-phase2", "proposal": "default", "protocol": "all", "sa-dst-address": "::", "sa-src-address": "::", "src-address": "::/0", "src-port": "any", "tunnel": "true" } */ // https://help.mikrotik.com/docs/display/ROS/IPsec#IPsec-Policies func ResourceIpIpsecPolicy() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/ipsec/policy"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("ph2_count", "ph2_state", "sa_dst_address", "sa_src_address"), "action": { Type: schema.TypeString, Optional: true, Description: "Specifies what to do with the packet matched by the policy.none - pass the packet unchanged.discard " + "- drop the packet.encrypt - apply transformations specified in this policy and it's SA.", ValidateFunc: validation.StringInSlice([]string{"discard", "encrypt", "none"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "active": { Type: schema.TypeBool, Computed: true, }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "dst_address": { Type: schema.TypeString, Optional: true, Description: "Destination address to be matched in packets. Applicable when tunnel mode (`tunnel=yes`) or " + "template (`template=yes`) is used.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "dst_port": { Type: schema.TypeString, Optional: true, Description: "Destination port to be matched in packets. If set to any all ports will be matched.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyDynamic: PropDynamicRo, "group": { Type: schema.TypeString, Optional: true, Description: "Name of the policy group to which this **template** is assigned.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyInvalid: PropInvalidRo, "ipsec_protocols": { Type: schema.TypeString, Optional: true, Description: "Specifies what combination of Authentication Header and Encapsulating Security Payload protocols " + "you want to apply to matched traffic.", ValidateFunc: validation.StringInSlice([]string{"ah", "esp"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "level": { Type: schema.TypeString, Optional: true, Description: "Specifies what to do if some of the SAs for this policy cannot be found:\n * use - skip this transform, " + "do not drop the packet, and do not acquire SA from IKE daemon;\n * require - drop the packet and acquire " + "SA;\n * unique - drop the packet and acquire a unique SA that is only used with this particular policy. It " + "is used in setups where multiple clients can sit behind one public IP address (clients behind NAT).", ValidateFunc: validation.StringInSlice([]string{"require", "unique", "use"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "peer": { Type: schema.TypeString, Optional: true, Description: "Name of the peer on which the policy applies.", }, "proposal": { Type: schema.TypeString, Optional: true, Description: "Name of the proposal template that will be sent by IKE daemon to establish SAs for this policy.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "protocol": { Type: schema.TypeString, Optional: true, Description: "IP packet protocol to match.", ValidateFunc: validation.StringInSlice([]string{"all", "dccp", "ddp", "egp", "encap", "etherip", "ggp", "gre", "hmp", "icmp", "icmpv6", "idpr-cmtp", "igmp", "ipencap", "ipip", "ipsec-ah", "ipsec-esp", "ipv6-encap", "ipv6-frag", "ipv6-nonxt", "ipv6-opts", "ipv6-route", "iso-tp4", "l2tp", "ospf", "pim", "pup", "rdp", "rspf", "rsvp", "sctp", "st", "tcp", "udp", "udp-lite", "vmtp", "vrrp", "xns-idp", "xtp"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "src_address": { Type: schema.TypeString, Optional: true, Description: "Source address to be matched in packets. Applicable when tunnel mode (`tunnel=yes`) or template " + "(`template=yes`) is used.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "src_port": { Type: schema.TypeString, Optional: true, Description: "Source port to be matched in packets. If set to any all ports will be matched.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "template": { Type: schema.TypeBool, Optional: true, Description: "Creates a template and assigns it to a specified policy group.Following parameters are used " + "by template:\n * group - name of the policy group to which this template is assigned;\n * src-address,\n * dst-address " + "- Requested subnet must match in both directions (for example 0.0.0.0/0 to allow all);\n * protocol - protocol " + "to match, if set to all, then any protocol is accepted;\n * proposal - SA parameters used for this template;\n * level " + "- useful when unique is required in setups with multiple clients behind NAT.", }, "tunnel": { Type: schema.TypeBool, Optional: true, Description: "Specifies whether to use tunnel mode.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_ipsec_policy_group.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*2", "default": "true", "name": "default" } */ // https://help.mikrotik.com/docs/display/ROS/IPsec#IPsec-Groups func ResourceIpIpsecPolicyGroup() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/ipsec/policy/group"), MetaId: PropId(Id), KeyDefault: PropDefaultRo, KeyName: PropName(""), KeyComment: PropCommentRw, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_ipsec_policy_group_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" ) const testIpIpsecPolicyGroup = "routeros_ip_ipsec_policy_group.test" func TestAccIpIpsecPolicyGroupTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/ipsec/policy/group", "routeros_ip_ipsec_policy_group"), Steps: []resource.TestStep{ { Config: testAccIpIpsecPolicyGroupConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpIpsecPolicyGroup), resource.TestCheckResourceAttr(testIpIpsecPolicyGroup, "name", "test-group"), ), }, { Config: testAccIpIpsecPolicyGroupConfig(), ResourceName: testIpIpsecPolicyGroup, ImportStateId: `name=test-group`, ImportState: true, ImportStateCheck: func(states []*terraform.InstanceState) error { if len(states) != 1 { return fmt.Errorf("more than 1 states received, only one expected") } return nil }, }, }, }) }) } } func testAccIpIpsecPolicyGroupConfig() string { return fmt.Sprintf(`%v resource "routeros_ip_ipsec_policy_group" "test" { name = "test-group" } `, providerConfig) } ================================================ FILE: routeros/resource_ip_ipsec_policy_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" ) const testIpIpsecPolicy = "routeros_ip_ipsec_policy.policy" func TestAccIpIpsecPolicyTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/ipsec/policy", "routeros_ip_ipsec_policy"), Steps: []resource.TestStep{ { Config: testAccIpIpsecPolicyConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpIpsecPolicy), resource.TestCheckResourceAttr(testIpIpsecPolicy, "dst_address", "0.0.0.0/0"), resource.TestCheckResourceAttr(testIpIpsecPolicy, "group", "test-group-p"), resource.TestCheckResourceAttr(testIpIpsecPolicy, "proposal", "default"), resource.TestCheckResourceAttr(testIpIpsecPolicy, "src_address", "0.0.0.0/0"), resource.TestCheckResourceAttr(testIpIpsecPolicy, "template", "true"), ), }, { Config: testAccIpIpsecPolicyConfig(), ResourceName: testIpIpsecPolicy, ImportStateId: `group=test-group-p`, ImportState: true, ImportStateCheck: func(states []*terraform.InstanceState) error { if len(states) != 1 { return fmt.Errorf("more than 1 states received, only one expected") } return nil }, }, }, }) }) } } func testAccIpIpsecPolicyConfig() string { return fmt.Sprintf(`%v resource "routeros_ip_ipsec_policy_group" "group-for-policy" { name = "test-group-p" } resource "routeros_ip_ipsec_policy" "policy" { dst_address = "0.0.0.0/0" group = routeros_ip_ipsec_policy_group.group-for-policy.name proposal = "default" src_address = "0.0.0.0/0" template = true } `, providerConfig) } ================================================ FILE: routeros/resource_ip_ipsec_profile.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*A", "default": "true", "dh-group": "modp2048,modp1024", "dpd-interval": "2m", "dpd-maximum-failures": "5", "enc-algorithm": "aes-128,3des", "hash-algorithm": "sha1", "lifetime": "1d", "name": "default", "nat-traversal": "true", "proposal-check": "obey" } */ // https://help.mikrotik.com/docs/display/ROS/IPsec#IPsec-Profiles func ResourceIpIpsecProfile() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/ipsec/profile"), MetaId: PropId(Id), "dh_group": { Type: schema.TypeSet, Optional: true, Description: "Diffie-Hellman group (cipher strength).", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"modp768", "modp1024", "modp1536", "modp2048", "modp3072", "modp4096", "modp6144", "modp8192", "ecp256", "ecp384", "ecp521", "x25519"}, false), }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "dpd_interval": { Type: schema.TypeString, Optional: true, Description: "Dead peer detection interval. If set to disable-dpd, dead peer detection will not be used.", DiffSuppressFunc: TimeEqual, }, "dpd_maximum_failures": { Type: schema.TypeInt, Optional: true, Description: "Maximum count of failures until peer is considered to be dead. Applicable if DPD is enabled.", ValidateFunc: validation.IntBetween(1, 100), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "enc_algorithm": { Type: schema.TypeSet, Optional: true, Description: "List of encryption algorithms that will be used by the peer.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"3des", "aes-128", "aes-192", "aes-256", "blowfish", "camellia-128", "camellia-192", "camellia-256", "des"}, false), }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "hash_algorithm": { Type: schema.TypeString, Optional: true, Description: "Hashing algorithm. SHA (Secure Hash Algorithm) is stronger, but slower. MD5 uses 128-bit key, " + "sha1-160bit key.", ValidateFunc: validation.StringInSlice([]string{"md5", "sha1", "sha256", "sha384", "sha512"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "lifebytes": { Type: schema.TypeInt, Optional: true, Description: "Phase 1 lifebytes is used only as administrative value which is added to proposal. Used in " + "cases if remote peer requires specific lifebytes value to establish phase 1.", }, "lifetime": { Type: schema.TypeString, Optional: true, Description: "Phase 1 lifetime: specifies how long the SA will be valid.", DiffSuppressFunc: TimeEqual, }, KeyName: PropName(""), "nat_traversal": { Type: schema.TypeBool, Optional: true, Description: "Use Linux NAT-T mechanism to solve IPsec incompatibility with NAT routers between IPsec peers. " + "This can only be used with ESP protocol (AH is not supported by design, as it signs the complete packet, " + "including the IP header, which is changed by NAT, rendering AH signature invalid). The method encapsulates " + "IPsec ESP traffic into UDP streams in order to overcome some minor issues that made ESP incompatible " + "with NAT.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "prf_algorithm": { Type: schema.TypeString, Optional: true, Description: "", ValidateFunc: validation.StringInSlice([]string{"auto", "sha1", "sha256", "sha384", "sha512"}, false), }, "proposal_check": { Type: schema.TypeString, Optional: true, Description: "Phase 2 lifetime check logic:\n * claim - take shortest of proposed and configured lifetimes and " + "notify initiator about it\n * exact - require lifetimes to be the same\n * obey - accept whatever is sent by an " + "initiator\n * strict - if the proposed lifetime is longer than the default then reject the proposal otherwise " + "accept a proposed lifetime.", ValidateFunc: validation.StringInSlice([]string{"claim", "exact", "obey", "strict"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_ipsec_profile_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" ) const testIpIpsecProfile = "routeros_ip_ipsec_profile.test" func TestAccIpIpsecProfileTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/ipsec/profile", "routeros_ip_ipsec_profile"), Steps: []resource.TestStep{ { Config: testAccIpIpsecProfileConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpIpsecProfile), resource.TestCheckResourceAttr(testIpIpsecProfile, "name", "test-profile"), resource.TestCheckResourceAttr(testIpIpsecProfile, "hash_algorithm", "sha256"), resource.TestCheckResourceAttr(testIpIpsecProfile, "enc_algorithm.#", "2"), resource.TestCheckResourceAttr(testIpIpsecProfile, "enc_algorithm.0", "aes-192"), resource.TestCheckResourceAttr(testIpIpsecProfile, "enc_algorithm.1", "aes-256"), resource.TestCheckResourceAttr(testIpIpsecProfile, "nat_traversal", "false"), ), }, { Config: testAccIpIpsecProfileConfig(), ResourceName: testIpIpsecProfile, ImportStateId: `name=test-profile`, ImportState: true, ImportStateCheck: func(states []*terraform.InstanceState) error { if len(states) != 1 { return fmt.Errorf("more than 1 states received, only one expected") } return nil }, }, }, }) }) } } func testAccIpIpsecProfileConfig() string { return fmt.Sprintf(`%v resource "routeros_ip_ipsec_profile" "test" { name = "test-profile" hash_algorithm = "sha256" enc_algorithm = ["aes-192", "aes-256"] dh_group = ["ecp384", "ecp521"] nat_traversal = false } `, providerConfig) } ================================================ FILE: routeros/resource_ip_ipsec_proposal.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*0", "auth-algorithms": "sha1", "default": "true", "disabled": "false", "enc-algorithms": "aes-256-cbc,aes-192-cbc,aes-128-cbc", "lifetime": "30m", "name": "default", "pfs-group": "modp1024" } */ // https://help.mikrotik.com/docs/display/ROS/IPsec#IPsec-Proposals func ResourceIpIpsecProposal() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/ipsec/proposal"), MetaId: PropId(Id), "auth_algorithms": { Type: schema.TypeSet, Optional: true, Description: "Allowed algorithms for authorization. SHA (Secure Hash Algorithm) is stronger but slower. " + "MD5 uses a 128-bit key, sha1-160bit key.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"md5", "null", "sha1", "sha256", "sha512"}, false), }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyComment: PropCommentRw, KeyDefault: PropDefaultRo, KeyDisabled: PropDisabledRw, "enc_algorithms": { Type: schema.TypeSet, Optional: true, Description: "Allowed algorithms and key lengths to use for SAs.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"null", "des", "3des", "aes-128-cbc", "aes-128-ctr", "aes-128-gcm", "aes-192-cbc", "aes-192-ctr", "aes-192-gcm", "aes-256-cbc", "aes-256-ctr", "aes-256-gcm", "blowfish", "camellia-128", "camellia-192", "camellia-256", "twofish", "chacha20poly1305"}, false), }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "lifetime": { Type: schema.TypeString, Optional: true, Description: "How long to use SA before throwing it out.", DiffSuppressFunc: TimeEqual, }, KeyName: PropName(""), "pfs_group": { Type: schema.TypeString, Optional: true, Description: "The diffie-Helman group used for Perfect Forward Secrecy.", ValidateFunc: validation.StringInSlice([]string{"ecp256", "ecp384", "ecp521", "modp768", "modp1024", "modp1536", "modp2048", "modp3072", "modp4096", "modp6144", "modp8192", "none"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_ipsec_proposal_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpIpsecProposal = "routeros_ip_ipsec_proposal.test" func TestAccIpIpsecProposalTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/ipsec/proposal", "routeros_ip_ipsec_proposal"), Steps: []resource.TestStep{ { Config: testAccIpIpsecProposalConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpIpsecProposal), resource.TestCheckResourceAttr(testIpIpsecProposal, "name", "NordVPN"), resource.TestCheckResourceAttr(testIpIpsecProposal, "pfs_group", "none"), resource.TestCheckResourceAttr(testIpIpsecProposal, "lifetime", "45m10s"), ), }, }, }) }) } } func testAccIpIpsecProposalConfig() string { return fmt.Sprintf(`%v resource "routeros_ip_ipsec_proposal" "test" { name = "NordVPN" pfs_group = "none" lifetime = "45m10s" } `, providerConfig) } ================================================ FILE: routeros/resource_ip_ipsec_settings.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* REST JSON */ // https://help.mikrotik.com/docs/display/ROS/IPsec#IPsec-Settings func ResourceIpIpsecSettings() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/ipsec/settings"), MetaId: PropId(Id), "accounting": { Type: schema.TypeBool, Optional: true, Description: "Whether to send RADIUS accounting requests to a RADIUS server. Applicable if EAP Radius " + "(`auth-method=eap-radius`) or pre-shared key with XAuth authentication method " + "(`auth-method=pre-shared-key-xauth`) is used.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "interim_update": { Type: schema.TypeString, Optional: true, Description: "The interval between each consecutive RADIUS accounting Interim update. Accounting must be " + "enabled.", DiffSuppressFunc: TimeEqual, }, "xauth_use_radius": { Type: schema.TypeBool, Optional: true, Description: "Whether to use Radius client for XAuth users or not. Property is only applicable to peers " + "using the IKEv1 exchange mode.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_ipsec_settings_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpIpsecSettings = "routeros_ip_ipsec_settings.test" func TestAccIpIpsecSettingsTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccIpIpsecSettingsConfig("true", "10s"), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpIpsecSettings), resource.TestCheckResourceAttr(testIpIpsecSettings, "xauth_use_radius", "true"), resource.TestCheckResourceAttr(testIpIpsecSettings, "interim_update", "10s"), ), }, { Config: testAccIpIpsecSettingsConfig("false", "0s"), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpIpsecSettings), resource.TestCheckResourceAttr(testIpIpsecSettings, "xauth_use_radius", "false"), resource.TestCheckResourceAttr(testIpIpsecSettings, "interim_update", "0s"), ), }, }, }) }) } } func testAccIpIpsecSettingsConfig(param1, param2 string) string { return fmt.Sprintf(`%v resource "routeros_ip_ipsec_settings" "test" { xauth_use_radius = %v interim_update = "%v" } `, providerConfig, param1, param2) } ================================================ FILE: routeros/resource_ip_nat_pmp.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) // ResourceNatPmpSettings https://help.mikrotik.com/docs/display/ROS/NAT-PMP func ResourceNatPmpSettings() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/nat-pmp"), MetaId: PropId(Id), KeyEnabled: PropEnabled("Enable NAT-PMP service."), } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_nat_pmp_interfaces.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) // ResourceNatPmpInterfaces https://help.mikrotik.com/docs/display/ROS/NAT-PMP func ResourceNatPmpInterfaces() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/nat-pmp/interfaces"), MetaId: PropId(Id), MetaSetUnsetFields: PropSetUnsetFields("type"), KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, "forced_ip": { Type: schema.TypeString, Optional: true, Description: "Allow specifying what public IP to use if the external interface has more than one IP available.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "interface": { Type: schema.TypeString, Required: true, Description: "Interface name on which PMP will be running on", }, "type": { Type: schema.TypeString, Optional: true, Description: "NAT-PMP interface type:" + "\n * external - the interface a global IP address is assigned to" + "\n * internal - router's local interface the clients are connected to", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_nat_pmp_interfaces_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testNatPmpInterfaces = "routeros_ip_nat_pmp_interfaces.test" func TestAccNatPmpInterfacesTest_basic(t *testing.T) { if !testCheckMinVersion(t, testNatPmpMinVersion) { t.Logf("Test skipped, the minimum required version is %v", testNatPmpMinVersion) return } for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccNatPmpInterfacesConfig("ether1", "external", `forced_ip = "0.0.0.0"`), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testNatPmpInterfaces), resource.TestCheckResourceAttr(testNatPmpInterfaces, "interface", "ether1"), resource.TestCheckResourceAttr(testNatPmpInterfaces, "type", "external"), resource.TestCheckResourceAttr(testNatPmpInterfaces, "forced_ip", "0.0.0.0"), ), }, { Taint: []string{testNatPmpInterfaces}, Config: testAccNatPmpInterfacesConfig("ether1", "internal", ""), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(testNatPmpInterfaces, "interface", "ether1"), resource.TestCheckResourceAttr(testNatPmpInterfaces, "type", "internal"), resource.TestCheckNoResourceAttr(testNatPmpInterfaces, "forced_ip"), ), }, }, }) }) } } func testAccNatPmpInterfacesConfig(s1, s2, s3 string) string { return fmt.Sprintf(`%v resource "routeros_ip_nat_pmp_interfaces" "test" { interface = "%v" type = "%v" %v } `, providerConfig, s1, s2, s3) } ================================================ FILE: routeros/resource_ip_nat_pmp_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testNatPmpMinVersion = "7.13" const testNatPmpSettings = "routeros_ip_nat_pmp.test" func TestAccNatPmpSettingsTest_basic(t *testing.T) { if !testCheckMinVersion(t, testNatPmpMinVersion) { t.Logf("Test skipped, the minimum required version is %v", testNatPmpMinVersion) return } for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccNatPmpSettingsConfig(true), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testNatPmpSettings), resource.TestCheckResourceAttr(testNatPmpSettings, "enabled", "true"), ), }, { Config: testAccNatPmpSettingsConfig(false), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(testNatPmpSettings, "enabled", "false"), ), }, }, }) }) } } func testAccNatPmpSettingsConfig(b bool) string { return fmt.Sprintf(`%v resource "routeros_ip_nat_pmp" "test" { enabled = %v } `, providerConfig, b) } ================================================ FILE: routeros/resource_ip_neighbor_discovery.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { "discover-interface-list": "LAN", "lldp-med-net-policy-vlan": "disabled", "mode": "tx-and-rx", "protocol": "cdp,lldp,mndp" } */ // https://help.mikrotik.com/docs/display/ROS/Neighbor+discovery func ResourceIpNeighborDiscoverySettings() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/neighbor/discovery-settings"), MetaId: PropId(Id), "discover_interface_list": { Type: schema.TypeString, Optional: true, Description: "Interface list on which members the discovery protocol will run on.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "discover_interval": { Type: schema.TypeString, Optional: true, Description: "An option to adjust the frequency at which neighbor discovery packets are transmitted. " + "The setting is available since RouterOS version 7.16.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "lldp_dcbx": { Type: schema.TypeBool, Optional: true, Description: "Whether to send Data Center Bridging Capabilities Exchange Protocol (DCBX) TLVs, which " + "allows to communicate switch QoS settings and capabilities with other neighboring devices using LLDP. " + "**Only applies to CRS3xx, CRS5xx, CCR2116 and CCR2216 devices.**", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "lldp_mac_phy_config": { Type: schema.TypeBool, Optional: true, Description: "Whether to send MAC/PHY Configuration/Status TLV in LLDP, which indicates the interface " + "capabilities, current setting of the duplex status, bit rate, and auto-negotiation. Only applies " + "to the Ethernet interfaces. While TLV is optional in LLDP, it is mandatory when sending LLDP-MED, " + "meaning this TLV will be included when necessary even though the property is configured as disabled.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "lldp_max_frame_size": { Type: schema.TypeBool, Optional: true, Description: "Whether to send Maximum Frame Size TLV in LLDP, which indicates the maximum frame size capability" + " of the interface in bytes (`l2mtu + 18`). Only applies to the Ethernet interfaces.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "lldp_med_net_policy_vlan": { Type: schema.TypeString, Optional: true, Description: "Advertised VLAN ID for LLDP-MED Network Policy TLV. This allows assigning a VLAN ID for " + "LLDP-MED capable devices, such as VoIP phones. The TLV will only be added to interfaces where LLDP-MED " + "capable devices are discovered. Other TLV values are predefined and cannot be changed:" + "\n * Application Type - Voice" + "\n * VLAN Type - Tagged" + "\n * L2 Priority - 0" + "\n * DSCP Priority - 0\n" + "When used together with the bridge interface, the (R/M)STP protocol should be enabled with protocol-mode setting.\n" + "Additionally, other neighbor discovery protocols (e.g. CDP) should be excluded using protocol setting to " + "avoid LLDP-MED misconfiguration.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "lldp_poe_power": { Type: schema.TypeBool, Optional: true, Description: "Two specific TLVs facilitate Power over Ethernet (PoE) management between Power Sourcing " + "Equipment (PSE) and Powered Devices (PD).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "lldp_vlan_info": { Type: schema.TypeBool, Optional: true, Description: "An option whether to send IEEE 802.1 Organizationally Specific TLVs in LLDP related to VLANs. " + "The setting is available since RouterOS version 7.16.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "mode": { Type: schema.TypeString, Optional: true, Description: "Selects the neighbor discovery packet sending and receiving mode. The setting is " + "available since RouterOS version 7.7.", ValidateFunc: validation.StringInSlice([]string{"rx-only", "tx-only", "tx-and-rx"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "protocol": { Type: schema.TypeSet, Optional: true, Description: "List of used discovery protocols.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"cdp", "lldp", "mndp"}, false), }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_neighbor_discovery_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpNeighborDiscoverySettings = "routeros_ip_neighbor_discovery_settings.test" func TestAccIpNeighborDiscoverySettingsTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccIpNeighborDiscoverySettingsConfig("static", "1", "rx-only", `[]`), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpNeighborDiscoverySettings), resource.TestCheckResourceAttr(testIpNeighborDiscoverySettings, "discover_interface_list", "static"), ), }, { Config: testAccIpNeighborDiscoverySettingsConfig("none", "disabled", "tx-and-rx", `["cdp", "lldp", "mndp"]`), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(testIpNeighborDiscoverySettings, "discover_interface_list", "none"), ), }, }, }) }) } } func testAccIpNeighborDiscoverySettingsConfig(iflist, lldp, mode, proto string) string { return fmt.Sprintf(`%v resource "routeros_ip_neighbor_discovery_settings" "test" { discover_interface_list = "%v" lldp_med_net_policy_vlan = "%v" mode = "%v" protocol = %v } `, providerConfig, iflist, lldp, mode, proto) } ================================================ FILE: routeros/resource_ip_pool.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) // ResourceIPPool https://help.mikrotik.com/docs/display/ROS/IP+Pools func ResourceIPPool() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/pool"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("available", "total", "used"), KeyComment: PropCommentRw, KeyName: PropNameForceNewRw, "next_pool": { Type: schema.TypeString, Optional: true, Description: "When address is acquired from pool that has no free addresses, and next-pool property is set " + "to another pool, then next IP address will be acquired from next-pool.", }, "ranges": { Type: schema.TypeList, Required: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: `IP address list of non-overlapping IP address ranges in form of: ` + `["from1-to1", "from2-to2", ..., "fromN-toN"]. ` + `For example, ["10.0.0.1-10.0.0.27", "10.0.0.32-10.0.0.47"]`, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, SchemaVersion: 1, StateUpgraders: []schema.StateUpgrader{ { Type: ResourceIPPoolV0().CoreConfigSchema().ImpliedType(), Upgrade: stateMigrationNameToId(resSchema[MetaResourcePath].Default.(string)), Version: 0, }, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_pool_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpPoolAddress = "routeros_ip_pool.test_pool" func TestAccIpPoolTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/pool", "routeros_ip_pool"), Steps: []resource.TestStep{ { Config: testAccIpPoolConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpPoolAddress), resource.TestCheckResourceAttr(testIpPoolAddress, "name", "test_pool"), ), }, }, }) }) } } func testAccIpPoolConfig() string { return providerConfig + ` resource "routeros_ip_pool" "test_pool" { name = "test_pool" ranges = ["10.0.0.100-10.0.0.200"] } ` } ================================================ FILE: routeros/resource_ip_pool_v0.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func ResourceIPPoolV0() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/pool"), MetaId: PropId(Name), KeyComment: PropCommentRw, KeyName: PropNameForceNewRw, "next_pool": { Type: schema.TypeString, Optional: true, Description: "When address is acquired from pool that has no free addresses, and next-pool property is set " + "to another pool, then next IP address will be acquired from next-pool.", }, "ranges": { Type: schema.TypeList, Required: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: `IP address list of non-overlapping IP address ranges in form of: ` + `["from1-to1", "from2-to2", ..., "fromN-toN"]. ` + `For example, ["10.0.0.1-10.0.0.27", "10.0.0.32-10.0.0.47"]`, }, }, } } ================================================ FILE: routeros/resource_ip_route.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) // ResourceIPRoute // https://help.mikrotik.com/docs/spaces/ROS/pages/328084/IP+Routing // https://help.mikrotik.com/docs/spaces/ROS/pages/59965493/routing+route func ResourceIPRoute() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/route"), MetaId: PropId(Id), "active": { Type: schema.TypeBool, Computed: true, Description: "A flag indicates whether the route is elected as Active and eligible to be added to the FIB.", }, // If the parameter is present in the request, the MT automatically marks the route as a blackhole. // To solve this problem, let's remove the default value and the parameter will be present in the query // only when explicitly specified in the configuration. "blackhole": { Type: schema.TypeBool, Optional: true, Description: "It's a blackhole route. If you need to cancel route marking, then simply delete the " + "parameter from the configuration of the TF. The value of the parameter (true or false) has no " + "effect on the MT processing logic.", }, "check_gateway": { Type: schema.TypeString, Optional: true, Description: "Currently used check-gateway option.", ValidateFunc: validation.StringInSlice([]string{"arp", "bfd", "bfd-multihop", "none", "ping"}, false), }, KeyComment: PropCommentRw, "connect": { Type: schema.TypeBool, Computed: true, Description: "The route is directly reachable.", }, "dhcp": { Type: schema.TypeBool, Computed: true, Description: "A flag indicates whether the route was added by the DHCP service.", }, KeyDisabled: PropDisabledRw, "distance": { Type: schema.TypeInt, Optional: true, Default: 1, Description: "Value used in route selection. Routes with smaller distance value are given preference.", ValidateFunc: validation.IntBetween(1, 255), }, "dst_address": { Type: schema.TypeString, Optional: true, Default: "0.0.0.0/0", // Without the default setting, a non-empty plan is returned. Description: "IP prefix of route, specifies destination addresses that this route can be used for.", ValidateFunc: validation.IsCIDR, }, KeyDynamic: PropDynamicRo, "ecmp": { Type: schema.TypeBool, Computed: true, Description: "A flag indicates whether the route is added as an Equal-Cost Multi-Path route in the FIB.", }, "gateway": { Type: schema.TypeString, Required: true, Description: "Array of IP addresses or interface names. Specifies which host or interface packets should " + "be sent to (IP | interface | IP%interface | IP@table[, IP | string, [..]]).", }, KeyHwOffloaded: PropHwOffloadedRo, "immediate_gw": { Type: schema.TypeString, Computed: true, Description: "Shows actual (resolved) gateway and interface that will be used for packet forwarding.", }, KeyInactive: PropInactiveRo, "local_address": { Type: schema.TypeString, Computed: true, Description: "Local IP address of the connected network.", }, "pref_src": { Type: schema.TypeString, Optional: true, Description: "Which of the local IP addresses to use for locally originated packets that are sent via this " + "route. Value of this property has no effect on forwarded packets. If value of this property is set " + "to IP address that is not local address of this router then the route will be inactive (in ROS v6, " + "ROS v7 allows IP spoofing).", ValidateFunc: validation.IsIPv4Address, }, "routing_table": { Type: schema.TypeString, Optional: true, Default: "main", Description: "Routing table this route belongs to.", }, "scope": { Type: schema.TypeInt, Optional: true, Default: 30, Description: "Used in nexthop resolution. Route can resolve nexthop only through routes that have scope " + "less than or equal to the target-scope of this route.", ValidateFunc: validation.IntBetween(0, 255), }, "static": { Type: schema.TypeBool, Computed: true, }, "suppress_hw_offload": { Type: schema.TypeBool, Optional: true, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "target_scope": { Type: schema.TypeInt, Optional: true, Default: 10, Description: "Used in nexthop resolution. This is the maximum value of scope for a route through which a " + "nexthop of this route can be resolved.", ValidateFunc: validation.IntBetween(0, 255), }, "vrf_interface": { Type: schema.TypeString, Computed: true, Optional: true, Description: "VRF interface name.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_route_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpRouteAddress = "routeros_ip_route.test_route" func TestAccIpRouteTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/route", "routeros_ip_route"), Steps: []resource.TestStep{ { Config: testAccIpRouteConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpRouteAddress), resource.TestCheckResourceAttr(testIpRouteAddress, "distance", "1"), resource.TestCheckResourceAttr(testIpRouteAddress, "dst_address", "10.0.0.0/24"), resource.TestCheckResourceAttr(testIpRouteAddress, "gateway", "192.168.103.1"), ), }, }, }) }) } } func testAccIpRouteConfig() string { return providerConfig + ` resource "routeros_ip_route" "test_route" { distance = 1 dst_address = "10.0.0.0/24" gateway = "192.168.103.1" check_gateway = "ping" } ` } ================================================ FILE: routeros/resource_ip_service.go ================================================ package routeros import ( "context" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*0", "address": "", "disabled": "false", "invalid": "false", "name": "telnet", "port": "23", "vrf": "main" }, { ".id": "*6", "address": "", "certificate": "https-cert", "disabled": "false", "invalid": "false", "name": "www-ssl", "port": "443", "tls-version": "any", "vrf": "main" }, */ // https://help.mikrotik.com/docs/display/ROS/Services func ResourceIpService() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/service"), MetaId: PropId(Name), "address": { Type: schema.TypeString, Optional: true, Default: "", Description: "List of IP/IPv6 prefixes from which the service is accessible.", DiffSuppressFunc: func(k, oldValue, newValue string, d *schema.ResourceData) bool { if oldValue == "" && newValue == "0.0.0.0/0" { return false } return oldValue == newValue }, }, "certificate": { Type: schema.TypeString, Optional: true, Description: "The name of the certificate used by a particular service. Applicable only for services " + "that depend on certificates ( www-ssl, api-ssl ).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, KeyInvalid: PropInvalidRo, "max_sessions": { Type: schema.TypeInt, Optional: true, Description: "Maximum number of concurrent connections to a particular service. This option is available in RouterOS starting from version 7.16.", ValidateFunc: validation.IntAtLeast(1), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "name": { Type: schema.TypeString, Computed: true, Description: "Service name.", }, "numbers": { Type: schema.TypeString, Required: true, Description: "The name of the service whose settings will be changed ( api, api-ssl, ftp, ssh, telnet, " + "winbox, www, www-ssl ).", ValidateDiagFunc: ValidationMultiValInSlice([]string{"api", "api-ssl", "ftp", "ssh", "telnet", "winbox", "www", "www-ssl"}, false, false), }, "port": { Type: schema.TypeInt, Required: true, Description: "The port particular service listens on.", ValidateFunc: validation.IntBetween(1, 65535), }, "proto": { Type: schema.TypeString, Computed: true, }, "tls_version": { Type: schema.TypeString, Optional: true, Description: "Specifies which TLS versions to allow by a particular service.", ValidateFunc: validation.StringInSlice([]string{"any", "only-1.2"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyVrf: PropVrfRw, } resCreateUpdate := func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { item, metadata := TerraformResourceDataToMikrotik(resSchema, d) d.SetId(d.Get("numbers").(string)) var resUrl string if m.(Client).GetTransport() == TransportREST { // https://router/rest/system/identity/set // https://router/rest/caps-man/manager/set resUrl = "/set" } err := m.(Client).SendRequest(crudPost, &URL{Path: metadata.Path + resUrl}, item, nil) if err != nil { return diag.FromErr(err) } return ResourceRead(ctx, resSchema, d, m) } return &schema.Resource{ CreateContext: resCreateUpdate, ReadContext: DefaultRead(resSchema), UpdateContext: resCreateUpdate, DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_service_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpServiceAddress = "routeros_ip_service.telnet" func TestAccIpServiceTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccIpServiceConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpServiceAddress), resource.TestCheckResourceAttr(testIpServiceAddress, "name", "telnet"), ), }, }, }) }) } } func testAccIpServiceConfig() string { return providerConfig + ` resource "routeros_ip_service" "telnet" { numbers = "telnet" disabled = true port = 23 } ` } ================================================ FILE: routeros/resource_ip_settings.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { "accept-redirects": "false", "accept-source-route": "false", "allow-fast-path": "true", "arp-timeout": "30s", "icmp-errors-use-inbound-interface-address": "false", "icmp-rate-limit": "10", "icmp-rate-mask": "0x1818", "ip-forward": "true", "ipv4-fast-path-active": "true", "ipv4-fast-path-bytes": "0", "ipv4-fast-path-packets": "0", "ipv4-fasttrack-active": "false", "ipv4-fasttrack-bytes": "0", "ipv4-fasttrack-packets": "0", "ipv4-multipath-hash-policy": "l3", "max-neighbor-entries": "8192", "rp-filter": "no", "secure-redirects": "true", "send-redirects": "true", "tcp-syncookies": "false", "tcp-timestamps": "random-offset" } */ // https://help.mikrotik.com/docs/spaces/ROS/pages/103841817/IP+Settings func ResourceIpSettings() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/settings"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("ipv4_fast_path_active", "ipv4_fast_path_bytes", "ipv4_fast_path_packets", "ipv4_fasttrack_active", "ipv4_fasttrack_bytes", "ipv4_fasttrack_packets"), "accept_redirects": { Type: schema.TypeBool, Optional: true, Description: "Whether to accept ICMP redirect messages. Typically should be enabled on the host and disabled " + "on routers.", }, "accept_source_route": { Type: schema.TypeBool, Optional: true, Description: "Whether to accept packets with the SRR option. Typically should be enabled on the router.", }, "allow_fast_path": { Type: schema.TypeBool, Optional: true, Description: "Allows Fast Path.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "arp_timeout": { Type: schema.TypeString, Optional: true, Description: "Sets Linux base_reachable_time (base_reachable_time_ms) on all interfaces that use ARP. " + "The initial validity of the ARP entry is picked from the interval [timeout/2 - 3*timeout/2] (default from " + "15s to 45s) after the neighbor was found. Can use postfix ms, s, m, h, d for milliseconds, seconds, " + "minutes, hours, or days. if no postfix is set then seconds (s) are used. The parameter means how long " + "a valid ARP record will be considered complete if no one communicates with the specific MAC/IP during " + "this time. The parameter does not represent a time when an ARP entry is removed from the ARP cache " + "(see max-neighbor-entries setting).", DiffSuppressFunc: TimeEqual, }, "icmp_errors_use_inbound_interface_address": { Type: schema.TypeBool, Optional: true, Description: "If enabled, the ICMP error message reply will be sent with the source address equal to primary " + "address of the receiving interface that caused the error . This feature can be useful for complex network " + "debugging.", }, "icmp_rate_limit": { Type: schema.TypeInt, Optional: true, Description: "Limit the maximum rates for sending ICMP packets whose type matches icmp-rate-mask to specific " + "targets. `0` disables any limiting, other values indicate the minimum space between responses in milliseconds.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "icmp_rate_mask": { Type: schema.TypeString, Optional: true, Description: "Mask made of ICMP types for which rates are being limited.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "ip_forward": { Type: schema.TypeBool, Optional: true, Description: "Enable/disable packet forwarding between interfaces. Resets all configuration parameters " + "to defaults according to RFC1812 for routers.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "ipv4_multipath_hash_policy": { Type: schema.TypeString, Optional: true, Description: "IPv4 Hash policy used for ECMP routing in `/ip/settings` menu" + "\n * l3 -- layer-3 hashing of src IP, dst IP" + "\n * l3-inner -- layer-3 hashing or inner layer-3 hashing if available" + "\n * l4 -- layer-4 hashing of src IP, dst IP, IP protocol, src port, dst port", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "max_neighbor_entries": { Type: schema.TypeInt, Optional: true, Description: "Sets Linux gc_thresh3. A maximum number of allowed neighbors in the ARP table. Since " + "`RouterOS version 7.1`, the default value depends on the installed amount of RAM. It is possible to set " + "a higher value than the default, but it increases the risk of out-of-memory condition." + "\nThe default values for certain RAM sizes:" + "\n * 2048 for 64 MB," + "\n * 4096 for 128 MB," + "\n * 8192 for 256 MB," + "\n * 16384 for 512 MB or higher." + "\nThe ARP cache stores ARP entries, and if some of these entries are incomplete, they can stay in the " + "cache for an indefinite period of time. This will only happen if the number of entries in the cache is " + "less than one-fourth of the maximum number allowed. The reason for this is to prevent the unnecessary " + "running of the garbage-collector when the ARP table is not close to being full.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "route_cache": { Type: schema.TypeBool, Optional: true, Description: "Disable or enable the Linux route cache. Note that disabling the route cache, will also " + "disable the fast path.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "rp_filter": { Type: schema.TypeString, Optional: true, Description: "Disables or enables source validation." + "\n * no - No source validation." + "\n * strict - Strict mode as defined in RFC3704 Strict Reverse Path. Each incoming packet is tested " + "against the FIB and if the interface is not the best reverse path the packet check will fail. By " + "default failed packets are discarded." + "\n * loose - Loose mode as defined in RFC3704 Loose Reverse Path. Each incoming packet's source " + "address is also tested against the FIB and if the source address is not reachable via any interface " + "the packet check will fail." + "\nThe current recommended practice in RFC3704 is to enable strict mode to prevent IP spoofing from DDoS " + "attacks. If using asymmetric routing or other complicated routing or VRRP, then the loose mode is recommended." + "\n`Warning`: strict mode does not work with routing tables", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "secure_redirects": { Type: schema.TypeBool, Optional: true, Description: "Accept ICMP redirect messages only for gateways, listed in the default gateway list.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "send_redirects": { Type: schema.TypeBool, Optional: true, Description: "Whether to send ICMP redirects. Recommended to be enabled on routers.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "tcp_timestamps": { Type: schema.TypeString, Optional: true, Description: "Parameter allows to enable/disable TCP timestamps or add random offset to TCP timestamp " + "(default behavior). Disabling timestamps completely may help to reduce spikes of performance drops.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "tcp_syncookies": { Type: schema.TypeBool, Optional: true, Description: "end out syncookies when the syn backlog queue of a socket overflows. This is to prevent " + "the common 'SYN flood attack'. syncookies seriously violate TCP protocol, and disallow the use of TCP " + "extensions, which can result in serious degradation of some services (f.e. SMTP relaying), visible not " + "by you, but to your clients and relays, contacting you.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_settings_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpSettings = "routeros_ip_settings.settings" func TestAccIpSettingsTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccIpSettingsConfig("false"), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpSettings), resource.TestCheckResourceAttr(testIpSettings, "allow_fast_path", "false"), ), }, { Config: testAccIpSettingsConfig("true"), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpSettings), resource.TestCheckResourceAttr(testIpSettings, "allow_fast_path", "true"), ), }, }, }) }) } } func testAccIpSettingsConfig(param string) string { return fmt.Sprintf(`%v resource "routeros_ip_settings" "settings" { allow_fast_path = %v } `, providerConfig, param) } ================================================ FILE: routeros/resource_ip_smb.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { "enabled": "auto", "status": "disabled", "domain": "MSHOME", "comment": "MikrotikSMB", "interfaces": "all" } */ // https://help.mikrotik.com/docs/spaces/ROS/pages/117145608/SMB func ResourceIpSMB() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/smb"), MetaId: PropId(Id), KeyEnabled: { Type: schema.TypeString, Optional: true, Description: "The default value is 'auto'. This means that the SMB server will automatically be enabled when the first non-disabled SMB share is configured under '/ip smb share'.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: ValidationAutoYesNo, }, "domain": { Type: schema.TypeString, Optional: true, Description: "Name of Windows Workgroup.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyComment: { // This is the SMB server comment, not a MikroTik comment, which is why this deserves its own attribute and not PropCommentRw. Type: schema.TypeString, Optional: true, Description: "Set comment for the server.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "interfaces": { Type: schema.TypeSet, Elem: &schema.Schema{ Type: schema.TypeString, }, Optional: true, Description: "List of interfaces on which SMB service will be running.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "status": { Type: schema.TypeString, Computed: true, }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_smb_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpSMBMinVersion = "7.14" const testIpSMBTask = "routeros_ip_smb.test" func TestAccIpSMBTest_basic(t *testing.T) { if !testCheckMinVersion(t, testIpSMBMinVersion) { t.Logf("Test skipped, the minimum required version is %v", testIpSMBMinVersion) return } for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccIpSMBConfig("auto", "MSHOME", "MikrotikSMB", "all"), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpSMBTask), resource.TestCheckResourceAttr(testIpSMBTask, "enabled", "auto"), resource.TestCheckResourceAttr(testIpSMBTask, "domain", "MSHOME"), resource.TestCheckResourceAttr(testIpSMBTask, "comment", "MikrotikSMB"), resource.TestCheckTypeSetElemAttr(testIpSMBTask, "interfaces.*", "all"), ), }, }, }) }) } } func testAccIpSMBConfig(enabled, domain, comment, _interface string) string { return fmt.Sprintf(`%v resource "routeros_ip_smb" "test" { enabled = "%v" domain = "%v" comment = "%v" interfaces = ["%v"] } `, providerConfig, enabled, domain, comment, _interface) } ================================================ FILE: routeros/resource_ip_ssh_server.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { "allow-none-crypto": "false", "always-allow-password-login": "false", "forwarding-enabled": "no", "host-key-size": "2048", "strong-crypto": "false" } */ // https://help.mikrotik.com/docs/display/ROS/SSH#SSH-SSHServer func ResourceIpSSHServer() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/ssh"), MetaId: PropId(Id), "allow_none_crypto": { Type: schema.TypeBool, Optional: true, Description: "Whether to allow connection if cryptographic algorithms are set to none.", ExactlyOneOf: []string{"allow_none_crypto", "strong_crypto"}, }, "always_allow_password_login": { Type: schema.TypeBool, Optional: true, Description: "Whether to allow password login at the same time when public key authorization is " + "configured for a user.", }, "ciphers": { Type: schema.TypeString, Optional: true, Description: "Allow to configure SSH ciphers.", ValidateFunc: validation.StringInSlice([]string{"3des-cbc", "aes-cbc", "aes-ctr", "aes-gcm", "auto"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "forwarding_enabled": { Type: schema.TypeString, Optional: true, Description: "Allows to control which SSH forwarding method to allow:" + "\n * no - SSH forwarding is disabled;" + "\n * local - Allow SSH clients to originate connections from the server(router), this setting controls also dynamic forwarding;" + "\n * remote - Allow SSH clients to listen on the server(router) and forward incoming connections;" + "\n * both - Allow both local and remote forwarding methods.", ValidateFunc: validation.StringInSlice([]string{"both", "local", "no", "remote"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "host_key_size": { Type: schema.TypeInt, Optional: true, Description: "RSA key size when host key is being regenerated.", ValidateFunc: validation.IntInSlice([]int{1024, 1536, 2048, 4096, 8192}), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "host_key_type": { Type: schema.TypeString, Optional: true, Description: "Select host key type.", ValidateFunc: validation.StringInSlice([]string{"rsa", "ed25519"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "publickey_authentication_options": { Type: schema.TypeString, Optional: true, Description: "Sets public key authentication options." + "\nThe touch-required option causes public key authentication using a FIDO authenticator " + "algorithm to always require the signature to attest that a physically present user explicitly" + "confirmed the authentication (usually by touching the authenticator)." + "\nThe verify-required option requires a FIDO key signature attest that the user was verified, e.g. via a PIN.", ValidateFunc: validation.StringInSlice([]string{"none", "touch-required", "verify-required"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "strong_crypto": { Type: schema.TypeBool, Optional: true, Description: "Use stronger encryption.", ExactlyOneOf: []string{"allow_none_crypto", "strong_crypto"}, }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_ssh_server_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpSSHServerSettings = "routeros_ip_ssh_server.test" func TestAccIpSSHServerSettingsTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccIpSSHServerSettingsConfig(true, "both", 1024), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpSSHServerSettings), resource.TestCheckResourceAttr(testIpSSHServerSettings, "allow_none_crypto", "true"), resource.TestCheckResourceAttr(testIpSSHServerSettings, "always_allow_password_login", "true"), resource.TestCheckResourceAttr(testIpSSHServerSettings, "forwarding_enabled", "both"), resource.TestCheckResourceAttr(testIpSSHServerSettings, "host_key_size", "1024"), ), }, { Config: testAccIpSSHServerSettingsConfig(false, "no", 2048), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(testIpSSHServerSettings, "allow_none_crypto", "false"), resource.TestCheckResourceAttr(testIpSSHServerSettings, "always_allow_password_login", "false"), resource.TestCheckResourceAttr(testIpSSHServerSettings, "forwarding_enabled", "no"), resource.TestCheckResourceAttr(testIpSSHServerSettings, "host_key_size", "2048"), ), }, }, }) }) } } func testAccIpSSHServerSettingsConfig(b bool, fwd string, kSize int) string { return fmt.Sprintf(`%v resource "routeros_ip_ssh_server" "test" { allow_none_crypto = %v always_allow_password_login = %v forwarding_enabled = "%v" host_key_size = %v } `, providerConfig, b, b, fwd, kSize) } ================================================ FILE: routeros/resource_ip_tftp.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "allow": "true", "allow-rollover": "false", "disabled": "false", "hits": 0, "ip-addresses": "10.0.0.0/24", "read-only": "true", "real-filename": "/usb1/file.txt", "req-filename": "file.txt" } */ // ResourceIPTFTP https://wiki.mikrotik.com/Manual:IP/TFTP func ResourceIpTFTP() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/tftp"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("hits"), "allow": { Type: schema.TypeBool, Optional: true, Description: "Allow connection.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "allow_overwrite": { Type: schema.TypeBool, Optional: true, Description: "If `true`, overwriting the file is allowed.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "allow_rollover": { Type: schema.TypeBool, Optional: true, Description: "If set, server will allow sequence number to roll over when " + "maximum value is reached. This is used to enable large " + "downloads using TFTP server.", }, KeyDisabled: PropDisabledRw, "ip_addresses": { Type: schema.TypeSet, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.Any( validation.IsCIDR, validation.IsIPAddress, ), }, Optional: true, Description: "Range of IP addresses accepted as clients. " + "If empty `0.0.0.0/0` will be used.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "read_only": { Type: schema.TypeBool, Optional: true, Description: "Sets if file can be written to. If set to `false` write " + "attempts will fail with an error.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "reading_window_size": { Type: schema.TypeString, Optional: true, Description: "TFTP Windowsize option value.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "real_filename": { Type: schema.TypeString, Optional: true, Description: "If `req-filename` and `real-filename` values are set and " + "valid, the requested filename will be replaced with " + "matched file. This field has to be set. If multiple regex " + "are specified in `req-filename`, with this field you can " + "set which ones should match, so this rule is validated. " + "`real-filename` format for using multiple regex is " + "`filename\\0\\5\\6`.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "req_filename": { Type: schema.TypeString, Optional: true, Description: "Requested filename as regular expression (regex) if field " + "is left empty it defaults to `.*`.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_tftp_settings.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* {"max-block-size": "4096"} */ // ResourceIPTFTPSettings https://wiki.mikrotik.com/Manual:IP/TFTP func ResourceIpTFTPSettings() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/tftp/settings"), MetaId: PropId(Name), "max_block_size": { Type: schema.TypeInt, Optional: true, Description: "Maximum accepted block size value. During transfer " + "negotiation phase, RouterOS device will not negotiate larger value " + "than this.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_tftp_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterfaceIpTFTPAddress = "routeros_ip_tftp.test_file" func TestAccInterfaceIpTFTPTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/tftp", "routeros_ip_tftp"), Steps: []resource.TestStep{ { Config: testAccIpTFTPConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceIpTFTPAddress), resource.TestCheckResourceAttr(testInterfaceIpTFTPAddress, "ip_addresses.0", "10.0.0.0/24"), resource.TestCheckResourceAttr(testInterfaceIpTFTPAddress, "req_filename", "file.txt"), resource.TestCheckResourceAttr(testInterfaceIpTFTPAddress, "real_filename", "/file.txt"), resource.TestCheckResourceAttr(testInterfaceIpTFTPAddress, "read_only", "true"), ), }, }, }) }) } } func testAccIpTFTPConfig() string { return providerConfig + ` resource "routeros_ip_tftp" "test_file" { ip_addresses = ["10.0.0.0/24"] req_filename = "file.txt" real_filename = "/file.txt" read_only = true } ` } ================================================ FILE: routeros/resource_ip_traffic_flow.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { "active-flow-timeout": "30m", "cache-entries": "128k", "enabled": "false", "inactive-flow-timeout": "15s", "interfaces": "all", "packet-sampling": "false", "sampling-interval": "0", "sampling-space": "0" } */ // https://help.mikrotik.com/docs/spaces/ROS/pages/21102653/Traffic+Flow#TrafficFlow-General func ResourceIpTrafficFlow() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/traffic-flow"), MetaId: PropId(Id), "active_flow_timeout": { Type: schema.TypeString, Optional: true, Description: "Maximum life-time of a flow.", DiffSuppressFunc: TimeEqual, }, "cache_entries": { Type: schema.TypeString, Optional: true, Description: "Number of flows which can be in router's memory simultaneously.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyEnabled: PropEnabled("Enable/Disable Traffic-Flow system"), "inactive_flow_timeout": { Type: schema.TypeString, Optional: true, Description: "How long to keep the flow active, if it is idle. If a connection does not see any packet within " + "this timeout, then traffic-flow will send a packet out as a new flow. If this timeout is too small it " + "can create a significant amount of flows and overflow the buffer.", DiffSuppressFunc: TimeEqual, }, "interfaces": { Type: schema.TypeString, Optional: true, Description: "Names of those interfaces will be used to gather statistics for traffic-flow. To specify more " + "than one interface, separate them with a comma.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "packet_sampling": { Type: schema.TypeBool, Optional: true, Description: "Enable or disable packet sampling feature.", }, "sampling_interval": { Type: schema.TypeInt, Optional: true, Description: "The number of packets that are consecutively sampled.", }, "sampling_space": { Type: schema.TypeInt, Optional: true, Description: "The number of packets that are consecutively omitted.", }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_traffic_flow_ipfix.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { "bytes": "true", "dst-address": "true", "dst-address-mask": "true", "dst-mac-address": "true", "dst-port": "true", "first-forwarded": "true", "gateway": "true", "icmp-code": "true", "icmp-type": "true", "igmp-type": "true", "in-interface": "true", "ip-header-length": "true", "ip-total-length": "true", "ipv6-flow-label": "true", "is-multicast": "true", "last-forwarded": "true", "nat-dst-address": "true", "nat-dst-port": "true", "nat-events": "false", "nat-src-address": "true", "nat-src-port": "true", "out-interface": "true", "packets": "true", "protocol": "true", "src-address": "true", "src-address-mask": "true", "src-mac-address": "true", "src-port": "true", "sys-init-time": "true", "tcp-ack-num": "true", "tcp-flags": "true", "tcp-seq-num": "true", "tcp-window-size": "true", "tos": "true", "ttl": "true", "udp-length": "true" } */ // https://help.mikrotik.com/docs/spaces/ROS/pages/21102653/Traffic+Flow#TrafficFlow-IPFIX func ResourceIpTrafficFlowIpfix() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/traffic-flow/ipfix"), MetaId: PropId(Id), MetaSetUnsetFields: PropSetUnsetFields("nat_events"), "bytes": { Type: schema.TypeString, Optional: true, Description: "Total number of bytes processed in the flow.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "dst_address": { Type: schema.TypeString, Optional: true, Description: "The destination IP address of the flow.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "dst_address_mask": { Type: schema.TypeString, Optional: true, Description: "Network mask for the destination address.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "dst_mac_address": { Type: schema.TypeString, Optional: true, Description: "Destination MAC address.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "dst_port": { Type: schema.TypeString, Optional: true, Description: "Destination port number.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "first_forwarded": { Type: schema.TypeString, Optional: true, Description: "Timestamp of the first packet forwarded in a flow.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "gateway": { Type: schema.TypeString, Optional: true, Description: "IP address of the gateway through which the flow was routed.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "icmp_code": { Type: schema.TypeString, Optional: true, Description: "ICMP code for error messaging and operational information.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "icmp_type": { Type: schema.TypeString, Optional: true, Description: "Type of ICMP message, important for diagnostic messages.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "igmp_type": { Type: schema.TypeString, Optional: true, Description: "Type of Internet Group Management Protocol operation.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "in_interface": { Type: schema.TypeString, Optional: true, Description: "Interface through which packets of the flow are received.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "ip_header_length": { Type: schema.TypeString, Optional: true, Description: "Length of the IP header.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "ip_total_lenght": { Type: schema.TypeString, Optional: true, Description: "Length of the IP packet in bytes.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "ipv6_flow_label": { Type: schema.TypeString, Optional: true, Description: "Label field from an IPv6 header, used to classify flows.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "is_multicast": { Type: schema.TypeString, Optional: true, Description: "Indicates whether the flow is a multicast flow.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "last_forwarded": { Type: schema.TypeString, Optional: true, Description: "Timestamp of the last packet forwarded in a flow.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "nat_dst_address": { Type: schema.TypeString, Optional: true, Description: "Translated destination IP address by NAT.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "nat_dst_port": { Type: schema.TypeString, Optional: true, Description: "Translated destination port number by NAT.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "nat_events": { Type: schema.TypeString, Optional: true, Description: "Events related to Network Address Translation for the flow.", }, "nat_src_address": { Type: schema.TypeString, Optional: true, Description: "Translated source IP address by NAT.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "nat_src_port": { Type: schema.TypeString, Optional: true, Description: "Translated source port number by NAT.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "out_interface": { Type: schema.TypeString, Optional: true, Description: "Interface through which packets of the flow are sent out.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "packets": { Type: schema.TypeString, Optional: true, Description: "Number of packets processed in the flow.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "protocol": { Type: schema.TypeString, Optional: true, Description: "Protocol number (e.g., TCP, UDP, ICMP).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "src_address": { Type: schema.TypeString, Optional: true, Description: "The source IP address of the flow.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "src_address_mask": { Type: schema.TypeString, Optional: true, Description: "Network mask for the source address, useful in summarizing data.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "src_mac_address": { Type: schema.TypeString, Optional: true, Description: "Source MAC address.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "src_port": { Type: schema.TypeString, Optional: true, Description: "Source port number.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "sys_init_time": { Type: schema.TypeString, Optional: true, Description: "System initialization time, can be used for timing analysis.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "tcp_ack_num": { Type: schema.TypeString, Optional: true, Description: "Acknowledgment number in a TCP connection.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "tcp_flags": { Type: schema.TypeString, Optional: true, Description: "Flags from the TCP header (e.g., SYN, ACK).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "tcp_seq_num": { Type: schema.TypeString, Optional: true, Description: "Sequence number in a TCP connection.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "tcp_window_size": { Type: schema.TypeString, Optional: true, Description: "Window size in a TCP connection, indicating the scale of received data buffering.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "tos": { Type: schema.TypeString, Optional: true, Description: "Type of Service field in the IP header, indicating priority and handling of the packet.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "ttl": { Type: schema.TypeString, Optional: true, Description: "Time To Live for the packet, decremented by each router to prevent infinite loops.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "udp_length": { Type: schema.TypeString, Optional: true, Description: "Length of the UDP payload.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_traffic_flow_ipfix_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpTrafficFlowIpfix = "routeros_ip_traffic_flow_ipfix.test" func TestAccIpTrafficFlowIpfixTest_basic(t *testing.T) { t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccIpTrafficFlowIpfixConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpTrafficFlowIpfix), resource.TestCheckResourceAttr(testIpTrafficFlowIpfix, "nat_events", "true"), resource.TestCheckResourceAttr(testIpTrafficFlowIpfix, "dst_port", "false"), ), }, { Config: testAccIpTrafficFlowIpfixConfigRollback(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpTrafficFlowIpfix), resource.TestCheckResourceAttr(testIpTrafficFlowIpfix, "nat_events", "false"), resource.TestCheckResourceAttr(testIpTrafficFlowIpfix, "dst_port", "true"), ), }, }, }) }) } } func testAccIpTrafficFlowIpfixConfig() string { return fmt.Sprintf(`%v resource "routeros_ip_traffic_flow_ipfix" "test" { nat_events = true dst_port = false } `, providerConfig) } func testAccIpTrafficFlowIpfixConfigRollback() string { return fmt.Sprintf(`%v resource "routeros_ip_traffic_flow_ipfix" "test" { nat_events = false dst_port = true } `, providerConfig) } ================================================ FILE: routeros/resource_ip_traffic_flow_target.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "disabled": "false", "dst-address": "0.0.0.0", "port": "2055", "src-address": "0.0.0.0", "v9-template-refresh": "20", "v9-template-timeout": "30m", "version": "9" } */ // https://help.mikrotik.com/docs/spaces/ROS/pages/21102653/Traffic+Flow#TrafficFlow-Targets func ResourceIpTrafficFlowTarget() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/traffic-flow/target"), MetaId: PropId(Id), KeyDisabled: PropDisabledRw, "dst_address": { Type: schema.TypeString, Optional: true, Description: "IP address of the host which receives Traffic-Flow statistic packets from the router.", ValidateFunc: validation.IsIPAddress, }, "port": { Type: schema.TypeInt, Optional: true, Description: "Port (UDP) of the host which receives Traffic-Flow statistic packets from the router.", ValidateFunc: Validation64k, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "src_address": { Type: schema.TypeString, Optional: true, Description: "IP address used as source when sending Traffic-Flow statistics.", ValidateFunc: validation.IsIPAddress, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "v9_template_refresh": { Type: schema.TypeInt, Optional: true, Description: "Number of packets after which the template is sent to the receiving host (only for NetFlow " + "version 9 and IPFIX).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "v9_template_timeout": { Type: schema.TypeString, Optional: true, Description: "After how long to send the template, if it has not been sent. (only for NetFlow version 9 " + "and IPFIX).", DiffSuppressFunc: TimeEqual, }, "version": { Type: schema.TypeString, Optional: true, Description: "Which version format of NetFlow to use.", ValidateFunc: validation.StringInSlice([]string{"1", "5", "9", "IPFIX"}, false), }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_traffic_flow_target_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpTrafficFlowTarget = "routeros_ip_traffic_flow_target.test" func TestAccIpTrafficFlowTargetTest_basic(t *testing.T) { t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/traffic-flow/target", "routeros_ip_traffic_flow_target"), Steps: []resource.TestStep{ { Config: testAccIpTrafficFlowTargetConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpTrafficFlowTarget), resource.TestCheckResourceAttr(testIpTrafficFlowTarget, "dst_address", "192.168.0.2"), resource.TestCheckResourceAttr(testIpTrafficFlowTarget, "port", "2055"), resource.TestCheckResourceAttr(testIpTrafficFlowTarget, "version", "9"), ), }, }, }) }) } } func testAccIpTrafficFlowTargetConfig() string { return fmt.Sprintf(`%v resource "routeros_ip_traffic_flow_target" "test" { dst_address = "192.168.0.2" port = 2055 version = "9" } `, providerConfig) } ================================================ FILE: routeros/resource_ip_traffic_flow_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpTrafficFlow = "routeros_ip_traffic_flow.test" func TestAccIpTrafficFlowTest_basic(t *testing.T) { t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccIpTrafficFlowConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpTrafficFlow), resource.TestCheckResourceAttr(testIpTrafficFlow, "packet_sampling", "true"), resource.TestCheckResourceAttr(testIpTrafficFlow, "sampling_interval", "2222"), resource.TestCheckResourceAttr(testIpTrafficFlow, "sampling_space", "1111"), ), }, { Config: testAccIpTrafficFlowConfigRollback(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpTrafficFlow), resource.TestCheckResourceAttr(testIpTrafficFlow, "packet_sampling", "false"), resource.TestCheckResourceAttr(testIpTrafficFlow, "sampling_interval", "0"), resource.TestCheckResourceAttr(testIpTrafficFlow, "sampling_space", "0"), ), }, }, }) }) } } func testAccIpTrafficFlowConfig() string { return fmt.Sprintf(`%v resource "routeros_ip_traffic_flow" "test" { packet_sampling = true sampling_interval = 2222 sampling_space = 1111 } `, providerConfig) } func testAccIpTrafficFlowConfigRollback() string { return fmt.Sprintf(`%v resource "routeros_ip_traffic_flow" "test" { packet_sampling = false sampling_interval = 0 sampling_space = 0 } `, providerConfig) } ================================================ FILE: routeros/resource_ip_upnp.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) // ResourceUPNPSettings https://help.mikrotik.com/docs/display/ROS/UPnP func ResourceUPNPSettings() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/upnp"), MetaId: PropId(Id), "allow_disable_external_interface": { Type: schema.TypeBool, Optional: true, Description: "Whether or not should the users be allowed to disable the router's external interface. " + "This functionality (for users to be able to turn the router's external interface off without any " + "authentication procedure) is required by the standard, but as it is sometimes not expected or " + "unwanted in UPnP deployments which the standard was not designed for (it was designed mostly for " + "home users to establish their own local networks), you can disable this behavior", }, KeyEnabled: PropEnabled("Enable UPnP service."), "show_dummy_rule": { Type: schema.TypeBool, Optional: true, Description: "Enable a workaround for some broken implementations, which are handling the absence of " + "UPnP rules incorrectly (for example, popping up error messages). This option will instruct the " + "server to install a dummy (meaningless) UPnP rule that can be observed by the clients, which refuse " + "to work correctly otherwise", }, } return &schema.Resource{ Description: "*If you do not disable the `allow-disable-external-interface`, any " + "user from the local network will be able (without any authentication procedures) to disable the router's " + "external interface.*", CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_upnp_interfaces.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) // ResourceUPNPInterfaces https://help.mikrotik.com/docs/display/ROS/UPnP func ResourceUPNPInterfaces() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/upnp/interfaces"), MetaId: PropId(Id), MetaSetUnsetFields: PropSetUnsetFields("type"), KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, "forced_ip": { Type: schema.TypeString, Optional: true, Description: "Allow specifying what public IP to use if the external interface has more than one IP available.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "interface": { Type: schema.TypeString, Required: true, Description: "Interface name on which UPnP will be running.", }, "type": { Type: schema.TypeString, Optional: true, Description: "UPnP interface type:" + "\n * external - the interface a global IP address is assigned to" + "\n * internal - router's local interface the clients are connected to", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_upnp_interfaces_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testUPNPInterfaces = "routeros_ip_upnp_interfaces.test" func TestAccUPNPInterfacesTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccUPNPInterfacesConfig("ether1", "external", `forced_ip = "0.0.0.0"`), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testUPNPInterfaces), resource.TestCheckResourceAttr(testUPNPInterfaces, "interface", "ether1"), resource.TestCheckResourceAttr(testUPNPInterfaces, "type", "external"), resource.TestCheckResourceAttr(testUPNPInterfaces, "forced_ip", "0.0.0.0"), ), }, { Taint: []string{testUPNPInterfaces}, Config: testAccUPNPInterfacesConfig("ether1", "internal", ""), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(testUPNPInterfaces, "interface", "ether1"), resource.TestCheckResourceAttr(testUPNPInterfaces, "type", "internal"), resource.TestCheckNoResourceAttr(testUPNPInterfaces, "forced_ip"), ), }, }, }) }) } } func testAccUPNPInterfacesConfig(s1, s2, s3 string) string { return fmt.Sprintf(`%v resource "routeros_ip_upnp_interfaces" "test" { interface = "%v" type = "%v" %v } `, providerConfig, s1, s2, s3) } ================================================ FILE: routeros/resource_ip_upnp_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testUPNPSettings = "routeros_ip_upnp.test" func TestAccUPNPSettingsTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccUPNPSettingsConfig(true), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testUPNPSettings), resource.TestCheckResourceAttr(testUPNPSettings, "allow_disable_external_interface", "true"), resource.TestCheckResourceAttr(testUPNPSettings, "enabled", "true"), resource.TestCheckResourceAttr(testUPNPSettings, "show_dummy_rule", "true"), ), }, { Config: testAccUPNPSettingsConfig(false), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(testUPNPSettings, "allow_disable_external_interface", "false"), resource.TestCheckResourceAttr(testUPNPSettings, "enabled", "false"), resource.TestCheckResourceAttr(testUPNPSettings, "show_dummy_rule", "false"), ), }, }, }) }) } } func testAccUPNPSettingsConfig(b bool) string { return fmt.Sprintf(`%v resource "routeros_ip_upnp" "test" { allow_disable_external_interface = %v enabled = %v show_dummy_rule = %v } `, providerConfig, b, b, b) } ================================================ FILE: routeros/resource_ip_vrf.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) // ResourceIPRoute https://wiki.mikrotik.com/wiki/Manual:Virtual_Routing_and_Forwarding func ResourceIPVrf() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ip/vrf"), MetaId: PropId(Id), KeyDisabled: PropDisabledRw, KeyComment: PropCommentRw, KeyName: PropName("Unique name of the VRF."), "interfaces": { Type: schema.TypeSet, Required: true, Description: "At least one interface must be added to the VRF.", Elem: &schema.Schema{ Type: schema.TypeString, }, MinItems: 1, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ip_vrf_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) func TestAccIpVrfTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ip/vrf", "routeros_ip_vrf"), Steps: []resource.TestStep{ { Config: testAccIpVrfConfig(), Check: resource.ComposeTestCheckFunc( // A testResourcePrimaryInstanceId("routeros_ip_vrf.test_vrf_a"), resource.TestCheckResourceAttr("routeros_ip_vrf.test_vrf_a", "disabled", "true"), resource.TestCheckResourceAttr("routeros_ip_vrf.test_vrf_a", "name", "vrf_1"), ), }, }, }) }) } } func testAccIpVrfConfig() string { return providerConfig + ` resource "routeros_interface_veth" "veth1" { name = "veth1" } resource "routeros_interface_veth" "veth2" { name = "veth2" } resource "routeros_ip_vrf" "test_vrf_a" { disabled = true name = "vrf_1" interfaces = ["veth1", "veth2"] depends_on = [routeros_interface_veth.veth1, routeros_interface_veth.veth2] } ` } ================================================ FILE: routeros/resource_ipv6_address.go ================================================ package routeros import ( "strings" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* [ { ".id": "*1", "actual-interface": "ether1", "address": "fe80::5605:abff:fecd:1231/64", "advertise": "false", "disabled": "false", "dynamic": "true", "eui-64": "false", "from-pool": "", "interface": "ether1", "invalid": "false", "link-local": "true", "no-dad": "false" } ] */ // ResourceIPv6Address https://help.mikrotik.com/docs/display/ROS/IP+Addressing func ResourceIPv6Address() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ipv6/address"), MetaId: PropId(Id), "address": { Type: schema.TypeString, Optional: true, Computed: true, Description: "IPv6 address. Using the eui_64 and from_pool options can transform the original address! " + "[See docs](https://wiki.mikrotik.com/wiki/Manual:IPv6/Address#Properties)", AtLeastOneOf: []string{"address", "from_pool"}, DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { // This check is very dirty, be careful! /* eui_64 == true or from_pool != "" After applying this test step, the plan was not empty. # routeros_ipv6_address.test_v6_address will be updated in-place ~ resource "routeros_ipv6_address" "test_v6_address" { ~ address = "fc00:3::5c30:77ff:fe61:33ac/64" -> "fc00:3::/64" id = "*1D" # (9 unchanged attributes hidden) } */ if old == new { return true } if old == "" || new == "" { return false } // k = address, old = fc00:3::5c30:77ff:fe61:33ac/64, new = fc00:3::/64 // eui_64: true, address: "fc00:3::/64" ===> "fc00:3::/64" -> "fc00:3::5c30:77ff:fe61:33ac/64" addr := strings.SplitN(new, "/", 2) if len(addr) == 2 { addr[1] = "/" + addr[1] } if len(addr) == 2 && len(old) >= len(new) { if old[:len(addr[0])] == addr[0] && old[len(old)-len(addr[1]):] == addr[1] { return true } } /* /ipv6/pool/print # NAME PREFIX PREFIX-LENGTH 0 pool1 fc00:3::/62 64 */ // from_pool: pool1, address: "::1/64" ===> "::1/64" -> "fc00:3::1/64" if len(old) >= len(new) && strings.HasSuffix(old, new) { return true } // eui_64: true, from_pool: pool1, address: "::1/64" ===> "::1/64" -> "fc00:3::5c30:77ff:fe61:33ac/64" // N.B. We consider this a configuration error until such a configuration is encountered by someone. // eui_64: true and from_pool: pool1 ===> "" -> "" return false }, }, "advertise": { Type: schema.TypeBool, Optional: true, Description: "Whether to enable stateless address configuration. The prefix of that address is " + "automatically advertised to hosts using ICMPv6 protocol. The option is set by default for addresses " + "with prefix length 64.", }, "actual_interface": { // RO Type: schema.TypeString, Computed: true, Description: "Name of the actual interface the logical one is bound to.", }, "auto_link_local": { Type: schema.TypeBool, Optional: true, Description: "If newly created address is manual link-local address this setting allows to override " + "dynamically created IPv6 link-local address.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, "deprecated": { Type: schema.TypeBool, Computed: true, Description: "Whether address is deprecated", }, "eui_64": { Type: schema.TypeBool, Optional: true, Description: "Whether to calculate EUI-64 address and use it as last 64 bits of the IPv6 address.", }, "from_pool": { Type: schema.TypeString, Optional: true, Description: "Name of the pool from which prefix will be taken to construct IPv6 address taking last part " + "of the address from address property.", AtLeastOneOf: []string{"address", "from_pool"}, }, "global": { // RO Type: schema.TypeBool, Computed: true, Description: "Whether address is global.", }, KeyInterface: PropInterfaceRw, KeyInvalid: PropInvalidRo, "link_local": { //RO Type: schema.TypeBool, Computed: true, Description: "Whether address is link local.", }, "no_dad": { Type: schema.TypeBool, Optional: true, Description: "If set indicates that address is anycast address and Duplicate Address Detection should " + "not be performed.", }, "slave": { Type: schema.TypeBool, Computed: true, Description: "Whether address belongs to an interface which is a slave port to some other master interface", }, KeyVrf: PropVrfRw, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ipv6_address_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterfaceIPv6AddressAddress = "routeros_ipv6_address.test_v6_address" func TestAccInterfaceIPv6AddressTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ipv6/address", "routeros_ipv6_address"), Steps: []resource.TestStep{ { Config: testAccInterfaceIPv6AddressConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceIPv6AddressAddress), resource.TestCheckResourceAttrWith(testInterfaceIPv6AddressAddress, "address", func(value string) error { if value[:7] != "fc00:3:" { return fmt.Errorf(`Attribute 'address' expected "fc00:3:", got "%s"`, value) } return nil }), ), }, }, }) }) } } func testAccInterfaceIPv6AddressConfig() string { return providerConfig + ` resource "routeros_ipv6_address" "test_v6_address" { interface = "bridge" address = "fc00:3::/64" advertise = true eui_64 = true no_dad = true // address = "::1/64" // from_pool = "pool1" } resource "routeros_ip_route" "d2_rmark_isp22" { distance = 2 gateway = "10.10.10.1" routing_table = "main" target_scope = 12 } ` } ================================================ FILE: routeros/resource_ipv6_dhcp_client.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* [ { ".id": "*1", "add-default-route": "false", "dhcp-options": "", "dhcp-server-v6": "fe80::", "disabled": "false", "duid": "0x000003434343443", "interface": "if-name", "invalid": "false", "pool-name": "blacknight-pub-addr", "pool-prefix-length": "64", "prefix": "2a01:----:/56, 6d16h56m8s", "prefix-hint": "::/0", "request": "prefix", "status": "bound", "use-peer-dns": "true" } ] */ // ResourceIPv6DhcpClient https://help.mikrotik.com/docs/display/ROS/DHCP#DHCP-DHCPv6Client func ResourceIPv6DhcpClient() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ipv6/dhcp-client"), MetaId: PropId(Id), "accept_prefix_without_address": { Type: schema.TypeBool, Optional: true, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "add_default_route": { Type: schema.TypeBool, Optional: true, Default: false, Description: "Whether to add default IPv6 route after a client connects.", }, "address": { Type: schema.TypeString, Computed: true, Description: "IPv6 address, which is assigned to DHCPv6 Client from the Server.", }, "allow_reconfigure": { Type: schema.TypeBool, Optional: true, Description: "Allow reconfigure messages.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "check_gateway": { Type: schema.TypeString, Optional: true, Description: "Method on how to check gateway reachability.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyComment: PropCommentRw, "custom_iana_id": { Type: schema.TypeString, Optional: true, Description: "Allow to specify custom IANA ID.", DiffSuppressFunc: HexEqual, }, "custom_iapd_id": { Type: schema.TypeString, Optional: true, Description: "Allow to specify custom IAPD ID.", DiffSuppressFunc: HexEqual, }, "default_route_distance": { Type: schema.TypeInt, Optional: true, Description: "Distance of default route. Applicable if add-default-route is set to yes.", ValidateFunc: validation.IntBetween(0, 255), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "default_route_tables": { Type: schema.TypeSet, Optional: true, Description: "List of routing tables to which default route must be added. Table name can be proceeded with " + "\":x\" where x would be the distance for the route to be installed with.", Elem: &schema.Schema{ Type: schema.TypeString, }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "dhcp_options": { Type: schema.TypeSet, Optional: true, Description: "Options that are sent to the DHCP server.", Elem: &schema.Schema{ Type: schema.TypeString, }, }, "dhcp_server_v6": { Type: schema.TypeString, Computed: true, Description: "The IPv6 address of the DHCP server", }, KeyDisabled: PropDisabledRw, "duid": { Type: schema.TypeString, Computed: true, Description: "Auto-generated DUID that is sent to the server. DUID is generated using one of the MAC " + "addresses available on the router.", }, KeyDynamic: PropDynamicRo, "expires_after": { Type: schema.TypeString, Computed: true, Description: "A time when the IPv6 prefix expires (specified by the DHCPv6 server).", }, "gateway": { Type: schema.TypeString, Computed: true, Description: "The IP address of the gateway which is assigned by DHCP server.", }, KeyInterface: PropInterfaceRw, KeyInvalid: PropInvalidRo, "pool_name": { Type: schema.TypeString, Optional: true, Description: "Name of the IPv6 pool in which received IPv6 prefix will be added", }, "pool_prefix_length": { Type: schema.TypeInt, Optional: true, Description: "Prefix length parameter that will be set for IPv6 pool in which received IPv6 prefix is " + "added. Prefix length must be greater than the length of the received prefix, otherwise, prefix-length " + "will be set to received prefix length + 8 bits.", ValidateFunc: validation.IntBetween(0, 128), }, "prefix": { Type: schema.TypeString, Computed: true, Description: "Shows received IPv6 prefix from DHCPv6-PD server", }, "prefix_address_lists": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "Names of the firewall address lists to which received prefix will be added.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "prefix_hint": { Type: schema.TypeString, Optional: true, Computed: true, Description: "Include a preferred prefix length.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "rapid_commit": { Type: schema.TypeBool, Optional: true, Description: "Enable DHCP rapid commit (fast address assignment)", }, "request": { Type: schema.TypeList, Required: true, Description: "To choose if the DHCPv6 request will ask for the address, info or the IPv6 prefix.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"info", "address", "prefix"}, false), }, }, "script": { Type: schema.TypeString, Optional: true, Description: "Run this script on the DHCP-client status change. Available variables:" + "\n * pd-valid - if the prefix is acquired by the client;" + "\n * pd-prefix - the prefix acquired by the client if any;" + "\n * na-valid - if the address is acquired by the client;" + "\n * na-address - the address acquired by the client if any." + "\n * options - array of received options (only ROSv7)", }, "status": { Type: schema.TypeString, Computed: true, Description: "Shows the status of DHCPv6 Client:" + "\n * stopped - dhcpv6 client is stopped" + "\n * searching - sending `solicit` and trying to get `advertise` Shows actual (resolved) gateway and " + "interface that will be used for packet forwarding.requesting - sent `request` waiting for `reply`" + "\n * bound - received `reply`. Prefix assigned." + "\n * renewing - sent `renew`, waiting for `reply`" + "\n * rebinding - sent `rebind`, waiting for `reply`" + "\n * error - reply was not received in time or some other error occurred." + "\n * stopping - sent `release`", }, "use_interface_duid": { Type: schema.TypeBool, Optional: true, Description: "Specifies the MAC address of the specified interface as the DHCPv6 client DUID.", }, "use_peer_dns": { Type: schema.TypeBool, Optional: true, Default: true, Description: "Whether to accept the DNS settings advertised by the IPv6 DHCP Server.", }, "validate_server_duid": { Type: schema.TypeBool, Optional: true, Description: "Whether to validate the DUID of the IPv6 DHCP Server.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ipv6_dhcp_client_option.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) // ResourceDhcpClient https://help.mikrotik.com/docs/display/ROS/DHCP#DHCP-DHCPClient func ResourceIPv6DhcpClientOption() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ipv6/dhcp-client/option"), MetaId: PropId(Id), KeyName: PropName("The name that will be used in dhcp-client."), "code": { Type: schema.TypeInt, Required: true, Description: "The dhcp-client option code.", }, "value": { Type: schema.TypeString, Optional: true, Description: "The dhcp-client option", }, "raw_value": { Type: schema.TypeString, Computed: true, Description: "raw_value is computed from value.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ipv6_dhcp_client_option_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIPv6DhcpClientOption = "routeros_ipv6_dhcp_client_option.test_dhcp" func TestAccIPv6DhcpClientOptionTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ipv6/dhcp-client/option", "routeros_ipv6_dhcp_client_option"), Steps: []resource.TestStep{ { Config: testAccIPv6DhcpClientOptionConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIPv6DhcpClientOption), resource.TestCheckResourceAttr(testIPv6DhcpClientOption, "name", "my-dhcp-option"), resource.TestCheckResourceAttr(testIPv6DhcpClientOption, "code", "60"), ), }, }, }) }) } } func testAccIPv6DhcpClientOptionConfig() string { return providerConfig + ` resource "routeros_ipv6_dhcp_client_option" "test_dhcp" { name = "my-dhcp-option" code = 60 } ` } ================================================ FILE: routeros/resource_ipv6_dhcp_client_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIPv6DhcpClient = "routeros_ipv6_dhcp_client.client" func TestAccIPv6DhcpClient_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ipv6/dhcp-client", "routeros_ipv6_dhcp_client"), Steps: []resource.TestStep{ { Config: testAccIPv6DhcpClientConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIPv6DhcpClient), resource.TestCheckResourceAttr(testIPv6DhcpClient, "interface", "ether1"), resource.TestCheckResourceAttr(testIPv6DhcpClient, "pool_name", "inet-provider-pool"), resource.TestCheckResourceAttr(testIPv6DhcpClient, "request.0", "prefix"), resource.TestCheckResourceAttr(testIPv6DhcpClient, "prefix_hint", "::/60"), ), }, }, }) }) } } func testAccIPv6DhcpClientConfig() string { return providerConfig + ` resource "routeros_ipv6_dhcp_client" "client" { request = ["prefix"] pool_name = "inet-provider-pool" pool_prefix_length = 64 interface = "ether1" prefix_hint = "::/60" } ` } ================================================ FILE: routeros/resource_ipv6_dhcp_server.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "address-pool": "ULA", "comment": "https://ula.ungleich.ch/random/", "dhcp-option": "dns", "disabled": "false", "duid": "0x00030001d401c330e280", "dynamic": "false", "interface": "span-bridge", "invalid": "false", "lease-time": "10m", "name": "server1", "preference": "255", "rapid-commit": "true", "route-distance": "1", "use-radius": "false" } */ // https://help.mikrotik.com/docs/display/ROS/DHCP#DHCP-DHCPv6Server func ResourceIpv6DhcpServer() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ipv6/dhcp-server"), MetaId: PropId(Id), "address_pool": { Type: schema.TypeString, Optional: true, Description: "IPv6 pool, from which to take IPv6 address for the clients. The prefix length of the pool " + "must be 128.", AtLeastOneOf: []string{"address_pool", "prefix_pool"}, }, "address_lists": { Type: schema.TypeSet, Optional: true, Description: "Firewall address lists to which the allocated addresses and prefixes will be added if the lease is bound.", Elem: &schema.Schema{ Type: schema.TypeString, }, }, "allow_dual_stack_queue": { Type: schema.TypeBool, Optional: true, Description: "Creates a single simple queue entry for both IPv4 and IPv6 addresses, and uses the MAC address " + "and DUID for identification. Requires IPv6 DHCP Server to have this option enabled as well to work properly.", }, "binding_script": { Type: schema.TypeString, Optional: true, Description: "A script that will be executed after binding is assigned or de-assigned. Internal `global` " + "variables that can be used in the script:\n - bindingBound - set to `1` if bound, otherwise set to `0`\n" + " - bindingServerName - dhcp server name\n - bindingDUID - DUID\n - bindingAddress - active " + "address\n - bindingPrefix - active prefix.", }, KeyComment: PropCommentRw, "dhcp_option": { Type: schema.TypeSet, Optional: true, Description: "Add additional DHCP options from option list.", Elem: &schema.Schema{ Type: schema.TypeString, }, }, KeyDisabled: PropDisabledRw, "duid": { Type: schema.TypeString, Computed: true, Description: "DUID value.", }, KeyDynamic: PropDynamicRo, "ignore_ia_na_bindings": { Type: schema.TypeBool, Optional: true, Description: "Do not reply to DHCPv6 address requests and process only prefixes. Without this setting even " + "if server does not have address-pool configured, it has to respond to client that there is no address " + "available for the client. That can lead up to the situation when DHCPv6 client requests address and " + "prefix in a loop.", }, "insert_queue_before": { Type: schema.TypeString, Optional: true, Description: "Specify where to place dynamic simple queue entries for static DCHP leases with a " + "rate-limit parameter set.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.StringInSlice([]string{"bottom", "first"}, false), }, "interface": { Type: schema.TypeString, Required: true, Description: "The interface on which server will be running.", }, KeyInvalid: PropInvalidRo, "lease_time": { Type: schema.TypeString, Optional: true, Description: "The time that a client may use the assigned address. The client will try to renew this address " + "after half of this time and will request a new address after the time limit expires.", DiffSuppressFunc: TimeEqual, }, KeyName: PropName("Reference name."), "parent_queue": { Type: schema.TypeString, Optional: true, Description: "A dynamically created queue for this lease will be configured as a child queue of the specified parent queue.", }, "preference": { Type: schema.TypeInt, Optional: true, Description: "", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "prefix_pool": { Type: schema.TypeString, Optional: true, Description: "IPv6 pool, from which to take IPv6 prefix for the clients.", AtLeastOneOf: []string{"address_pool", "prefix_pool"}, }, "rapid_commit": { Type: schema.TypeBool, Optional: true, Description: "", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "route_distance": { Type: schema.TypeInt, Optional: true, Description: "Distance of the route.", ValidateFunc: validation.IntBetween(1, 255), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "use_radius": { Type: schema.TypeBool, Optional: true, Description: "Whether to use RADIUS server.", }, "use_reconfigure": { Type: schema.TypeBool, Optional: true, Description: "Allow the server to send Reconfigure messages to clients.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ipv6_dhcp_server_option.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*80000004", "code": "24", "name": "domain-search", "raw-value": "076578616d706c65056c6f63616c00", "value": "0x07'example'0x05'local'0x00" } */ // https://help.mikrotik.com/docs/display/ROS/DHCP#DHCP-DHCPOptions.1 // https://www.ipamworldwide.com/ipam/isc-dhcpv6-options.html // https://jjjordan.github.io/dhcp119/ func ResourceIpv6DhcpServerOption() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ipv6/dhcp-server/option"), MetaId: PropId(Id), "code": { Type: schema.TypeInt, Required: true, Description: "Dhcp option [code](https://www.ipamworldwide.com/ipam/isc-dhcpv6-options.html).", ValidateFunc: validation.IntBetween(1, 254), }, KeyComment: PropCommentRw, KeyName: PropName("Descriptive name of the option."), "value": { Type: schema.TypeString, Optional: true, Description: "Parameter's value. Available data types for options are:\n" + " - `'test'` -> ASCII to Hex 0x74657374\n" + " - `'10.10.10.10'` -> Unicode IP to Hex 0x0a0a0a0a\n" + " - `s'10.10.10.10'` -> ASCII to Hex 0x31302e31302e31302e3130\n" + " - `s'160'` -> ASCII to Hex 0x313630\n" + " - `'10'` -> Decimal to Hex 0x0a\n" + " - `0x0a0a` -> No conversion\n" + " - `$(VARIABLE)` -> hardcoded values\n\n" + " RouterOS has predefined variables that can be used:\n" + " - `HOSTNAME` - client hostname\n" + " - `RADIUS_MT_STR1` - from radius MT attr nr. `24`\n" + " - `RADIUS_MT_STR2` - from radius MT attr nr. `25`\n" + " - `REMOTE_ID` - agent remote-id\n" + " - `NETWORK_GATEWAY - the first gateway from `/ip dhcp-server network`, note that this option " + "won't work if used from lease.\n\nNow it is also possible to combine data types into one, for example: " + "`0x01'vards'$(HOSTNAME)`For example if HOSTNAME is 'kvm', then raw value will be 0x0176617264736b766d.", }, "raw_value": { Type: schema.TypeString, Computed: true, Description: "Read-only field which shows raw DHCP option value (the format actually sent out).", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ipv6_dhcp_server_option_sets.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*1", "name": "set1", "options": "dns,option1" } */ // https://help.mikrotik.com/docs/display/ROS/ func ResourceIpv6DhcpServerOptionSets() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ipv6/dhcp-server/option/sets"), MetaId: PropId(Id), KeyComment: PropCommentRw, KeyName: PropName("The name of the DHCPv6 option."), "options": { Type: schema.TypeSet, Optional: true, Description: "The list of options.", Elem: &schema.Schema{ Type: schema.TypeString, }, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ipv6_dhcp_server_option_sets_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpv6DhcpServerOptionSets = "routeros_ipv6_dhcp_server_option_sets.test" func TestAccIpv6DhcpServerOptionSetsTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ipv6/dhcp-server/option/sets", "routeros_ipv6_dhcp_server_option_sets"), Steps: []resource.TestStep{ { Config: testAccIpv6DhcpServerOptionSetsConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpv6DhcpServerOptionSets), resource.TestCheckResourceAttr(testIpv6DhcpServerOptionSets, "name", "test-set"), resource.TestCheckResourceAttr(testIpv6DhcpServerOptionSets, "options.#", "1"), resource.TestCheckResourceAttr(testIpv6DhcpServerOptionSets, "options.0", "domain-search-o24"), ), }, }, }) }) } } func testAccIpv6DhcpServerOptionSetsConfig() string { return fmt.Sprintf(`%v resource "routeros_ipv6_dhcp_server_option" "domain-search" { name = "domain-search-o24" code = 24 value = "0x07'example'0x05'local'0x00" } resource "routeros_ipv6_dhcp_server_option_sets" "test" { name = "test-set" options = [routeros_ipv6_dhcp_server_option.domain-search.name] } `, providerConfig) } ================================================ FILE: routeros/resource_ipv6_dhcp_server_option_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpv6DhcpServerOption = "routeros_ipv6_dhcp_server_option.test" func TestAccIpv6DhcpServerOptionTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ipv6/dhcp-server/option", "routeros_ipv6_dhcp_server_option"), Steps: []resource.TestStep{ { Config: testAccIpv6DhcpServerOptionConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpv6DhcpServerOption), resource.TestCheckResourceAttr(testIpv6DhcpServerOption, "name", "domain-search"), resource.TestCheckResourceAttr(testIpv6DhcpServerOption, "code", "24"), resource.TestCheckResourceAttr(testIpv6DhcpServerOption, "value", "0x07'example'0x05'local'0x00"), ), }, }, }) }) } } func testAccIpv6DhcpServerOptionConfig() string { return fmt.Sprintf(`%v resource "routeros_ipv6_dhcp_server_option" "test" { name = "domain-search" code = 24 value = "0x07'example'0x05'local'0x00" } `, providerConfig) } ================================================ FILE: routeros/resource_ipv6_dhcp_server_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpv6DhcpServerMinVersion = "7.20" const testIpv6DhcpServer = "routeros_ipv6_dhcp_server.test" func TestAccIpv6DhcpServerTest_basic(t *testing.T) { if !testCheckMinVersion(t, testIpv6DhcpServerMinVersion) { t.Logf("Test skipped, the minimum required version is %v", testIpv6DhcpServerMinVersion) return } for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ipv6/dhcp-server", "routeros_ipv6_dhcp_server"), Steps: []resource.TestStep{ { Config: testAccIpv6DhcpServerConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpv6DhcpServer), resource.TestCheckResourceAttr(testIpv6DhcpServer, "address_pool", "test-pool-0"), resource.TestCheckResourceAttr(testIpv6DhcpServer, "prefix_pool", "test-pool-1"), resource.TestCheckResourceAttr(testIpv6DhcpServer, "interface", "bridge"), resource.TestCheckResourceAttr(testIpv6DhcpServer, "lease_time", "1m"), resource.TestCheckResourceAttr(testIpv6DhcpServer, "name", "test-dhcpv6"), resource.TestCheckResourceAttr(testIpv6DhcpServer, "preference", "128"), ), }, }, }) }) } } func testAccIpv6DhcpServerConfig() string { return fmt.Sprintf(`%v resource "routeros_ipv6_pool" "pool-0" { name = "test-pool-0" prefix = "2001:db8:40::/65" prefix_length = 128 } resource "routeros_ipv6_pool" "pool-1" { name = "test-pool-1" prefix = "2001:db8:12::/65" prefix_length = 128 } resource "routeros_ipv6_dhcp_server" "test" { address_pool = routeros_ipv6_pool.pool-0.name prefix_pool = routeros_ipv6_pool.pool-1.name address_lists = ["test-list-0", "test-list-1"] interface = "bridge" lease_time = "1m" name = "test-dhcpv6" preference = 128 } `, providerConfig) } ================================================ FILE: routeros/resource_ipv6_firewall_addr_list.go ================================================ package routeros import ( "fmt" "time" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*1", "address": "123:dead::beaf/64", "creation-time": "sep/29/2022 07:09:24", "disabled": "false", "dynamic": "true", "list": "AAA", "timeout": "0s" } */ // ResourceIPv6FirewallAddrList https://help.mikrotik.com/docs/display/ROS/Address-lists // They work more or less the same as IPv4 address lists, except no ranges func ResourceIPv6FirewallAddrList() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ipv6/firewall/address-list"), MetaId: PropId(Id), "address": { Type: schema.TypeString, Required: true, Description: "A single IPv6 address or IPv6 CIDR subnet", DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { if old == new { return true } if old == "" || new == "" { return false } return old == fmt.Sprintf("%s/128", new) }, }, KeyComment: PropCommentRw, "creation_time": { Type: schema.TypeString, Computed: true, Description: "Rule creation time", }, KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, "list": { Type: schema.TypeString, Required: true, Description: "Name for the address list of the added IPv6 address.", }, "timeout": { Type: schema.TypeString, Optional: true, Description: `Time after address will be removed from address list. If timeout is not specified, the address will be stored into the address list permanently. > Please plan your work logic based on the fact that after the timeout > the resource has been destroyed outside of a Terraform. `, DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { if old == new { return true } if old == "" || new == "" { return false } // Compare intervals: oDuration, err := ParseDuration(old, time.Second) if err != nil { panic("[FirewallAddrList Timeout] parse 'old' duration error: " + err.Error()) } nDuration, err := ParseDuration(new, time.Second) if err != nil { panic("[FirewallAddrList Timeout] parse 'new' duration error: " + err.Error()) } // old new // ~ timeout = "4m59s" -> "5m" return nDuration.Seconds() > oDuration.Seconds() }, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ipv6_firewall_addr_list_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIPv6FirewallAddrList = "routeros_ipv6_firewall_addr_list.data" func TestAccIPv6FirewallAddrListTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ipv6/firewall/address-list", "routeros_ipv6_firewall_addr_list"), Steps: []resource.TestStep{ { Config: testAccIPv6FirewallAddrListConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIPv6FirewallAddrList), resource.TestCheckResourceAttr(testIPv6FirewallAddrList, "list", "test-addr-list"), resource.TestCheckResourceAttr(testIPv6FirewallAddrList, "address", "123:dead:beaf::/64"), ), }, }, }) }) } } func testAccIPv6FirewallAddrListConfig() string { return providerConfig + ` resource "routeros_ipv6_firewall_addr_list" "data" { list = "test-addr-list" address = "123:dead:beaf::/64" timeout = "5m" } ` } ================================================ FILE: routeros/resource_ipv6_firewall_filter.go ================================================ package routeros import ( "regexp" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) // ResourceIPv6FirewallFilter https://help.mikrotik.com/docs/display/ROS/Filter#Filter-Properties.1 func ResourceIPv6FirewallFilter() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ipv6/firewall/filter"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("bytes", "packets"), MetaSetUnsetFields: PropSetUnsetFields("dst_address_list", "src_address_list", "in_interface_list", "out_interface_list", "in_bridge_port_list", "out_bridge_port_list", "protocol"), "action": { Type: schema.TypeString, Required: true, Description: "Action to take if a packet is matched by the rule", ValidateFunc: validation.StringInSlice([]string{ "accept", "add-dst-to-address-list", "add-src-to-address-list", "drop", "fasttrack-connection", "jump", "log", "passthrough", "reject", "return", }, false), }, // Mikrotik v7.7 response - 400: 'Bad Request' (invalid time value for argument address-list-timeout) // request body: {"action":"drop","address-list-timeout":"none-dynamic", ...} // The default value is empty and the field is Computed. "address_list_timeout": { Type: schema.TypeString, Optional: true, Computed: true, // Default: "none-dynamic", Description: "Time interval after which the address will be removed from the address list specified by " + "address-list parameter. Used in conjunction with add-dst-to-address-list or add-src-to-address-list " + "actions.", }, "chain": { Type: schema.TypeString, Required: true, Description: "Specifies to which chain rule will be added. If the input does not match the name of an " + "already defined chain, a new chain will be created.", }, KeyComment: PropCommentRw, "connection_bytes": { Type: schema.TypeString, Optional: true, Description: "Matches packets only if a given amount of bytes has been transfered through the particular " + "connection.", }, "connection_limit": { Type: schema.TypeString, Optional: true, Description: "Matches connections per address or address block after given value is reached. Should be " + "used together with connection-state=new and/or with tcp-flags=syn because matcher is very resource " + "intensive.", }, "connection_mark": { Type: schema.TypeString, Optional: true, Description: "Matches packets marked via mangle facility with particular connection mark. If no-mark is " + "set, rule will match any unmarked connection.", }, // No NAT for IPv6. // See comment for the "path_cost" field in resource_interface_bridge_port.go file. "connection_rate": { Type: schema.TypeString, Optional: true, Description: "Connection Rate is a firewall matcher that allow to capture traffic based on present speed " + "of the connection (0..4294967295).", }, "connection_state": { Type: schema.TypeString, Optional: true, Description: "Interprets the connection tracking analysis data for a particular packet.", ValidateDiagFunc: ValidationMultiValInSlice([]string{ "established", "invalid", "new", "related", "untracked", }, false, true), }, "connection_type": { Type: schema.TypeString, Optional: true, Description: "Matches packets from related connections based on information from their connection " + "tracking helpers.", ValidateDiagFunc: ValidationMultiValInSlice([]string{ "ftp", "h323", "irc", "pptp", "quake3", "sip", "tftp", }, false, true), }, "content": { Type: schema.TypeString, Optional: true, Description: "Match packets that contain specified text.", }, KeyDisabled: PropDisabledRw, "dscp": { Type: schema.TypeInt, Optional: true, Description: "Matches DSCP IP header field.", ValidateFunc: validation.IntBetween(0, 63), }, "dst_address": { Type: schema.TypeString, Optional: true, Description: "Matches packets which destination is equal to specified IP or falls into specified IP range.", DiffSuppressFunc: ImplicitSingleHostCIDR6, }, "dst_address_list": { Type: schema.TypeString, Optional: true, Description: "Matches destination address of a packet against user-defined address list.", }, "dst_address_type": { Type: schema.TypeString, Optional: true, Description: "Matches destination address type.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"unicast", "local", "broadcast", "multicast"}, false, true), }, "dst_limit": { Type: schema.TypeString, Optional: true, Description: "Matches packets until a given rate is exceeded.", }, "dst_port": { Type: schema.TypeString, Optional: true, Description: "List of destination port numbers or port number ranges.", }, KeyDynamic: PropDynamicRo, // No fragment, hotspot, hw-offload. "headers": { Type: schema.TypeString, Optional: true, Description: "Extension headers. Look at the Extras tab in the v6 filter rules.", }, "hop_limit": { Type: schema.TypeString, Optional: true, Description: "IPv6 TTL. Look at the Extras tab in the v6 filter rules.", }, "icmp_options": { Type: schema.TypeString, Optional: true, Description: "Matches ICMP type: code fields.", }, "in_bridge_port": { Type: schema.TypeString, Optional: true, Description: "Actual interface the packet has entered the router if the incoming interface is a bridge. " + "Works only if use-ip-firewall is enabled in bridge settings.", }, "in_bridge_port_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as in-bridge-port.", }, "in_interface": { Type: schema.TypeString, Optional: true, Description: "Interface the packet has entered the router.", }, "in_interface_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as in-interface.", }, "ingress_priority": { Type: schema.TypeInt, Optional: true, Description: "Matches the priority of an ingress packet. Priority may be derived from VLAN, WMM, DSCP, " + "or MPLS EXP bit.", ValidateFunc: validation.IntBetween(0, 63), }, "invalid": { Type: schema.TypeBool, Computed: true, }, "ipsec_policy": { Type: schema.TypeString, Optional: true, Description: "Matches the policy used by IPsec. Value is written in the following format: direction, policy.", ValidateFunc: validation.StringMatch(regexp.MustCompile(`^(in|out)\s?,\s?(ipsec|none)$`), "Value must be written in the following format: direction, policy."), }, "jump_target": { Type: schema.TypeString, Optional: true, Description: "Name of the target chain to jump to. Applicable only if action=jump.", }, // No layer7-protocol. "limit": { Type: schema.TypeString, Optional: true, Description: "Matches packets up to a limited rate (packet rate or bit rate). A rule using this matcher " + "will match until this limit is reached. Parameters are written in the following format: " + "rate[/time],burst:mode.", }, "log": { Type: schema.TypeBool, Optional: true, Default: false, Description: "Add a message to the system log.", }, "log_prefix": { Type: schema.TypeString, Optional: true, Description: "Adds specified text at the beginning of every log message. Applicable if action=log or " + "log=yes configured.", }, "nth": { Type: schema.TypeString, Optional: true, Description: "Matches every nth packet: nth=2,1 rule will match every first packet of 2, hence, 50% of " + "all the traffic that is matched by the rule", }, "out_bridge_port": { Type: schema.TypeString, Optional: true, Description: "Actual interface the packet is leaving the router if the outgoing interface is a bridge. " + "Works only if use-ip-firewall is enabled in bridge settings.", }, "out_bridge_port_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as out-bridge-port.", }, "out_interface": { Type: schema.TypeString, Optional: true, Description: "Interface the packet is leaving the router.", }, "out_interface_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as out-interface.", }, "packet_mark": { Type: schema.TypeString, Optional: true, Description: "Matches packets marked via mangle facility with particular packet mark. If no-mark is set, " + "the rule will match any unmarked packet.", }, "packet_size": { Type: schema.TypeString, Optional: true, Description: "Matches packets of specified size or size range in bytes.", }, "per_connection_classifier": { Type: schema.TypeString, Optional: true, Description: "PCC matcher allows dividing traffic into equal streams with the ability to keep packets " + "with a specific set of options in one particular stream.", }, KeyPlaceBefore: PropPlaceBefore, "port": { Type: schema.TypeString, Optional: true, Description: "Matches if any (source or destination) port matches the specified list of ports or port " + "ranges. Applicable only if protocol is TCP or UDP", }, "priority": { Type: schema.TypeInt, Optional: true, Description: "Matches the packet's priority after a new priority has been set. Priority may be derived from " + "VLAN, WMM, DSCP, MPLS EXP bit, or from the priority that has been set using the set-priority action.", ValidateFunc: validation.IntBetween(0, 63), }, "protocol": { Type: schema.TypeString, Optional: true, Description: "Matches particular IP protocol specified by protocol name or number.", }, // No psd. "random": { Type: schema.TypeInt, Optional: true, Description: "Matches packets randomly with a given probability.", ValidateFunc: validation.IntBetween(1, 99), }, "reject_with": { Type: schema.TypeString, Optional: true, Description: "Specifies ICMP error to be sent back if the packet is rejected. Applicable if action=reject.", ValidateFunc: validation.StringInSlice([]string{ "icmp-address-unreachable", "icmp-admin-prohibited", "icmp-err-src-routing-header", "icmp-headers-too-long", "icmp-no-route", "icmp-not-neighbour", "icmp-port-unreachable", "tcp-reset", }, false), }, // Was removed? No information. // "routing_table": { // Type: schema.TypeString, // Optional: true, // Description: "Matches packets which destination address is resolved in specific a routing table.", // }, "routing_mark": { Type: schema.TypeString, Optional: true, Description: "Matches packets marked by mangle facility with particular routing mark.", }, "src_address": { Type: schema.TypeString, Optional: true, Description: "Matches packets which source is equal to specified IP or falls into a specified IP range.", DiffSuppressFunc: ImplicitSingleHostCIDR6, }, "src_address_list": { Type: schema.TypeString, Optional: true, Description: "Matches source address of a packet against user-defined address list.", }, "src_address_type": { Type: schema.TypeString, Optional: true, Description: "Matches source address type.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"unicast", "local", "broadcast", "multicast"}, false, true), }, "src_port": { Type: schema.TypeString, Optional: true, Description: "List of source ports and ranges of source ports. Applicable only if a protocol is TCP or UDP.", }, "src_mac_address": { Type: schema.TypeString, Optional: true, Description: "Matches source MAC address of the packet.", ValidateFunc: ValidationMacAddress, }, "tcp_flags": { Type: schema.TypeString, Optional: true, Description: "Matches specified TCP flags.", }, "tcp_mss": { Type: schema.TypeString, Optional: true, Description: "Matches TCP MSS value of an IP packet.", }, "time": { Type: schema.TypeString, Optional: true, Description: "Allows to create a filter based on the packets' arrival time and date or, for locally " + "generated packets, departure time and date.", }, "tls_host": { Type: schema.TypeString, Optional: true, Description: "Allows matching HTTPS traffic based on TLS SNI hostname.", }, // No TTL. } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ipv6_firewall_filter_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIPv6FirewallFilterAddress = "routeros_ipv6_firewall_filter.rule" func TestAccIPv6FirewallFilterTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ipv6/firewall/filter", "routeros_ipv6_firewall_filter"), Steps: []resource.TestStep{ { Config: testAccIPv6FirewallFilterConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIPv6FirewallFilterAddress), resource.TestCheckResourceAttr(testIPv6FirewallFilterAddress, "action", "drop"), ), }, }, }) }) } } func testAccIPv6FirewallFilterConfig() string { return providerConfig + ` resource "routeros_ipv6_firewall_filter" "rule" { // add action=drop chain=forward comment="" hop-limit= protocol=icmpv6 action = "drop" chain = "forward" comment = "Check drop hop-limit=1 + ipv6 multicast" src_address = "ff00::/8" hop_limit = "equal:1" protocol = "icmpv6" } ` } ================================================ FILE: routeros/resource_ipv6_firewall_mangle.go ================================================ package routeros import ( "context" "regexp" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "action": "accept", "bytes": "31611", "chain": "prerouting", "disabled": "false", "dynamic": "false", "invalid": "false", "log": "false", "log-prefix": "", "packets": "325" } */ // ResourceIPv6FirewallMangle https://wiki.mikrotik.com/wiki/Manual:IP/Firewall/Mangle func ResourceIPv6FirewallMangle() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ipv6/firewall/mangle"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("bytes", "packets"), MetaSetUnsetFields: PropSetUnsetFields("dst_address_list", "src_address_list", "in_interface", "in_interface_list", "out_interface", "out_interface_list", "in_bridge_port_list", "out_bridge_port_list"), "action": { Type: schema.TypeString, Required: true, Description: "Action to take if a packet is matched by the rule.", ValidateFunc: validation.StringInSlice([]string{ "accept", "add-dst-to-address-list", "add-src-to-address-list", "change-dscp", "change-mss", "change-ttl", "clear-df", "fasttrack-connection", "jump", "log", "mark-connection", "mark-packet", "mark-routing", "passthrough", "return", "route", "set-priority", "sniff-pc", "sniff-tzsp", "strip-ipv4-options", }, false), }, "address_list": { Type: schema.TypeString, Optional: true, Description: "Name of the address list to be used. Applicable if action is add-dst-to-address-list or " + "add-src-to-address-list.", }, // Mikrotik v7.7 response - 400: 'Bad Request' (invalid time value for argument address-list-timeout) // request body: {"action":"drop","address-list-timeout":"none-dynamic", ...} // The default value is empty and the field is Computed. "address_list_timeout": { Type: schema.TypeString, Optional: true, Computed: true, // Default: "none-dynamic", Description: "Time interval after which the address will be removed from the address list specified by " + "address-list parameter. Used in conjunction with add-dst-to-address-list or add-src-to-address-list " + "actions.", }, "chain": { Type: schema.TypeString, Required: true, Description: "Specifies to which chain rule will be added. If the input does not match the name of an " + "already defined chain, a new chain will be created.", }, KeyComment: PropCommentRw, "connection_bytes": { Type: schema.TypeString, Optional: true, Description: "Matches packets only if a given amount of bytes has been transfered through the particular " + "connection.", }, "connection_limit": { Type: schema.TypeString, Optional: true, Description: "Matches connections per address or address block after given value is reached. Should be " + "used together with connection-state=new and/or with tcp-flags=syn because matcher is very resource " + "intensive.", }, "connection_mark": { Type: schema.TypeString, Optional: true, Description: "Matches packets marked via mangle facility with particular connection mark. If no-mark is " + "set, rule will match any unmarked connection.", }, "connection_nat_state": { Type: schema.TypeString, Optional: true, Description: "Can match connections that are srcnatted, dstnatted or both.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"srcnat", "dstnat"}, false, true), }, // See comment for the "path_cost" field in resource_interface_bridge_port.go file. "connection_rate": { Type: schema.TypeString, Optional: true, Description: "Connection Rate is a firewall matcher that allow to capture traffic based on present speed " + "of the connection (0..4294967295).", }, "connection_state": { Type: schema.TypeString, Optional: true, Description: "Interprets the connection tracking analysis data for a particular packet.", ValidateDiagFunc: ValidationMultiValInSlice([]string{ "established", "invalid", "new", "related", "untracked", }, false, true), }, "connection_type": { Type: schema.TypeString, Optional: true, Description: "Matches packets from related connections based on information from their connection " + "tracking helpers.", ValidateDiagFunc: ValidationMultiValInSlice([]string{ "ftp", "h323", "irc", "pptp", "quake3", "sip", "tftp", }, false, true), }, "content": { Type: schema.TypeString, Optional: true, Description: "Match packets that contain specified text.", }, KeyDisabled: PropDisabledRw, "dscp": { Type: schema.TypeInt, Optional: true, Description: "Matches DSCP IP header field.", ValidateFunc: validation.IntBetween(0, 63), }, "dst_address": { Type: schema.TypeString, Optional: true, Description: "Matches packets which destination is equal to specified IP or falls into specified IP range.", DiffSuppressFunc: ImplicitSingleHostCIDR6, }, "dst_address_list": { Type: schema.TypeString, Optional: true, Description: "Matches destination address of a packet against user-defined address list.", }, "dst_address_type": { Type: schema.TypeString, Optional: true, Description: "Matches destination address type.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"unicast", "local", "broadcast", "multicast", "blackhole"}, false, true), }, "dst_limit": { Type: schema.TypeString, Optional: true, Description: "Matches packets until a given rate is exceeded.", }, "dst_port": { Type: schema.TypeString, Optional: true, Description: "List of destination port numbers or port number ranges.", }, KeyDynamic: PropDynamicRo, "icmp_options": { Type: schema.TypeString, Optional: true, Description: "Matches ICMP type: code fields.", }, "in_bridge_port": { Type: schema.TypeString, Optional: true, Description: "Actual interface the packet has entered the router if the incoming interface is a bridge. " + "Works only if use-ip-firewall is enabled in bridge settings.", }, "in_bridge_port_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as in-bridge-port.", }, "in_interface": { Type: schema.TypeString, Optional: true, Description: "Interface the packet has entered the router.", }, "in_interface_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as in-interface.", }, "ingress_priority": { Type: schema.TypeInt, Optional: true, Description: "Matches the priority of an ingress packet. Priority may be derived from VLAN, WMM, DSCP, " + "or MPLS EXP bit.", ValidateFunc: validation.IntBetween(0, 63), }, "invalid": { Type: schema.TypeBool, Computed: true, }, "ipsec_policy": { Type: schema.TypeString, Optional: true, Description: "Matches the policy used by IPsec. Value is written in the following format: direction, policy.", ValidateFunc: validation.StringMatch(regexp.MustCompile(`^(in|out)\s?,\s?(ipsec|none)$`), "Value must be written in the following format: direction, policy."), }, "limit": { Type: schema.TypeString, Optional: true, Description: "Matches packets up to a limited rate (packet rate or bit rate). A rule using this matcher " + "will match until this limit is reached. Parameters are written in the following format: " + "rate[/time],burst:mode.", }, "log": { Type: schema.TypeBool, Optional: true, Default: false, Description: "Add a message to the system log.", }, "log_prefix": { Type: schema.TypeString, Optional: true, Description: "Adds specified text at the beginning of every log message. Applicable if action=log or " + "log=yes configured.", }, "new_connection_mark": { Type: schema.TypeString, Optional: true, Description: "Sets a new connection-mark value.", }, "new_dscp": { Type: schema.TypeInt, Optional: true, Description: "Sets a new DSCP value for a packet.", ValidateFunc: validation.IntBetween(0, 63), }, "new_mss": { Type: schema.TypeString, Optional: true, Description: `Sets a new MSS for a packet. > clamp-to-pmtu feature sets (DF) bit in the IP header to dynamically discover the PMTU of a path. > Host sends all datagrams on that path with the DF bit set until receives ICMP. > Destination Unreachable messages with a code meaning "fragmentation needed and DF set". > Upon receipt of such a message, the source host reduces its assumed PMTU for the path. `, ValidateFunc: validation.StringMatch(regexp.MustCompile(`^(\d+|clamp-to-pmtu)$`), `Value must be a number in quotes or the string "clamp-to-pmtu".`), }, "new_packet_mark": { Type: schema.TypeString, Optional: true, Description: "Sets a new packet-mark value.", }, "new_priority": { Type: schema.TypeString, Optional: true, Description: "Sets a new priority for a packet. This can be the VLAN, WMM, DSCP or MPLS EXP priority. " + "This property can also be used to set an internal priority.", }, "new_routing_mark": { Type: schema.TypeString, Optional: true, Description: "Sets a new routing-mark value.", }, "new_ttl": { Type: schema.TypeString, Optional: true, Description: "Sets a new TTL for a packet.", }, "nth": { Type: schema.TypeString, Optional: true, Description: "Matches every nth packet: nth=2,1 rule will match every first packet of 2, hence, 50% of " + "all the traffic that is matched by the rule", }, "out_bridge_port": { Type: schema.TypeString, Optional: true, Description: "Actual interface the packet is leaving the router if the outgoing interface is a bridge. " + "Works only if use-ip-firewall is enabled in bridge settings.", }, "out_bridge_port_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as out-bridge-port.", }, "out_interface": { Type: schema.TypeString, Optional: true, Description: "Interface the packet is leaving the router.", }, "out_interface_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as out-interface.", }, "packet_mark": { Type: schema.TypeString, Optional: true, Description: "Matches packets marked via mangle facility with particular packet mark. If no-mark is set, " + "the rule will match any unmarked packet.", }, "packet_size": { Type: schema.TypeString, Optional: true, Description: "Matches packets of specified size or size range in bytes.", }, "passthrough": { Type: schema.TypeBool, Optional: true, Computed: true, Description: "Whether to let the packet to pass further (like action passthrough) into the " + "firewall or not (property only valid some actions).", }, "per_connection_classifier": { Type: schema.TypeString, Optional: true, Description: "PCC matcher allows dividing traffic into equal streams with the ability to keep packets " + "with a specific set of options in one particular stream.", }, KeyPlaceBefore: PropPlaceBefore, "port": { Type: schema.TypeString, Optional: true, Description: "Matches if any (source or destination) port matches the specified list of ports or port " + "ranges. Applicable only if protocol is TCP or UDP", }, "priority": { Type: schema.TypeInt, Optional: true, Description: "Matches the packet's priority after a new priority has been set. Priority may be derived from " + "VLAN, WMM, DSCP, MPLS EXP bit, or from the priority that has been set using the set-priority action.", ValidateFunc: validation.IntBetween(0, 63), }, "protocol": { Type: schema.TypeString, Optional: true, Description: "Matches particular IP protocol specified by protocol name or number.", }, "random": { Type: schema.TypeInt, Optional: true, Description: "Matches packets randomly with a given probability.", ValidateFunc: validation.IntBetween(1, 99), }, "routing_mark": { Type: schema.TypeString, Optional: true, Description: "Matches packets marked by mangle facility with particular routing mark.", }, "src_address": { Type: schema.TypeString, Optional: true, Description: "Matches packets which source is equal to specified IP or falls into a specified IP range.", DiffSuppressFunc: ImplicitSingleHostCIDR6, }, "src_address_list": { Type: schema.TypeString, Optional: true, Description: "Matches source address of a packet against user-defined address list.", }, "src_address_type": { Type: schema.TypeString, Optional: true, Description: "Matches source address type.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"unicast", "local", "broadcast", "multicast"}, false, true), }, "src_port": { Type: schema.TypeString, Optional: true, Description: "List of source ports and ranges of source ports. Applicable only if a protocol is TCP or UDP.", }, "src_mac_address": { Type: schema.TypeString, Optional: true, Description: "Matches source MAC address of the packet.", ValidateFunc: validation.IsMACAddress, }, "tcp_flags": { Type: schema.TypeString, Optional: true, Description: "Matches specified TCP flags.", }, "tcp_mss": { Type: schema.TypeString, Optional: true, Description: "Matches TCP MSS value of an IP packet.", }, "time": { Type: schema.TypeString, Optional: true, Description: "Allows to create a filter based on the packets' arrival time and date or, for locally " + "generated packets, departure time and date.", }, "tls_host": { Type: schema.TypeString, Optional: true, Description: "Allows matching HTTPS traffic based on TLS SNI hostname.", }, "ttl": { Type: schema.TypeString, Optional: true, Description: "Matches packets TTL value.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { skip := resSchema[MetaSkipFields].Default.(string) resSchema[MetaSkipFields].Default = skip + `,"place_before"` defer func() { resSchema[MetaSkipFields].Default = skip }() return ResourceUpdate(ctx, resSchema, d, m) }, DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ipv6_firewall_mangle_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIPv6FirewallMangle = "routeros_ipv6_firewall_mangle.data" func TestAccIPv6FirewallMangleTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ipv6/firewall/mangle", "routeros_ipv6_firewall_mangle"), Steps: []resource.TestStep{ { Config: testAccIPv6FirewallMangleConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIPv6FirewallMangle), resource.TestCheckResourceAttr(testIPv6FirewallMangle, "chain", "prerouting"), resource.TestCheckResourceAttr(testIPv6FirewallMangle, "action", "mark-connection"), resource.TestCheckResourceAttr(testIPv6FirewallMangle, "new_connection_mark", "test-mark"), ), }, }, }) }) } } func testAccIPv6FirewallMangleConfig() string { return providerConfig + ` resource "routeros_ipv6_firewall_mangle" "data" { chain = "prerouting" action = "mark-connection" new_connection_mark = "test-mark" comment = "new-mangle-rule" } ` } ================================================ FILE: routeros/resource_ipv6_firewall_nat.go ================================================ package routeros import ( "context" "regexp" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*2", "action": "masquerade", "bytes": "0", "chain": "srcnat", "disabled": "false", "dynamic": "false", "invalid": "false", "log": "false", "log-prefix": "", "out-interface": "all-wireless", "packets": "0", "src-address-list": "LAN" } */ // ResourceIPv6FirewallNat https://help.mikrotik.com/docs/display/ROS/NAT func ResourceIPv6FirewallNat() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ipv6/firewall/nat"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("bytes", "packets"), MetaSetUnsetFields: PropSetUnsetFields("dst_address_list", "src_address_list", "in_interface", "in_interface_list", "out_interface", "out_interface_list", "in_bridge_port_list", "out_bridge_port_list"), "action": { Type: schema.TypeString, Required: true, Description: "Action to take if a packet is matched by the rule", ValidateFunc: validation.StringInSlice([]string{ "accept", "add-dst-to-address-list", "add-src-to-address-list", "dst-nat", "endpoint-independent-nat", "jump", "log", "masquerade", "netmap", "passthrough", "redirect", "return", "same", "src-nat", }, false), }, "address_list": { Type: schema.TypeString, Optional: true, Description: "Name of the address list to be used. Applicable if action is add-dst-to-address-list or " + "add-src-to-address-list.", }, "address_list_timeout": { Type: schema.TypeString, Optional: true, Description: "Time interval after which the address will be removed from the address list specified by " + "address-list parameter. Used in conjunction with add-dst-to-address-list or add-src-to-address-list " + "actions.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "chain": { Type: schema.TypeString, Required: true, Description: "Specifies to which chain rule will be added. If the input does not match the name of an " + "already defined chain, a new chain will be created.", }, KeyComment: PropCommentRw, "connection_bytes": { Type: schema.TypeString, Optional: true, Description: "Matches packets only if a given amount of bytes has been transfered through the particular " + "connection.", }, "connection_limit": { Type: schema.TypeString, Optional: true, Description: "Matches connections per address or address block after given value is reached. Should be " + "used together with connection-state=new and/or with tcp-flags=syn because matcher is very resource " + "intensive.", }, "connection_mark": { Type: schema.TypeString, Optional: true, Description: "Matches packets marked via mangle facility with particular connection mark. If no-mark is " + "set, rule will match any unmarked connection.", }, // See comment for the "path_cost" field in resource_interface_bridge_port.go file. "connection_rate": { Type: schema.TypeString, Optional: true, Description: "Connection Rate is a firewall matcher that allow to capture traffic based on present speed " + "of the connection (0..4294967295).", }, "connection_type": { Type: schema.TypeString, Optional: true, Description: "Matches packets from related connections based on information from their connection " + "tracking helpers.", ValidateDiagFunc: ValidationMultiValInSlice([]string{ "ftp", "h323", "irc", "pptp", "quake3", "sip", "tftp", }, false, true), }, "content": { Type: schema.TypeString, Optional: true, Description: "Match packets that contain specified text.", }, KeyDisabled: PropDisabledRw, "dscp": { Type: schema.TypeInt, Optional: true, Description: "Matches DSCP IP header field.", ValidateFunc: validation.IntBetween(0, 63), }, "dst_address": { Type: schema.TypeString, Optional: true, Description: "Matches packets which destination is equal to specified IP or falls into specified IP range.", DiffSuppressFunc: ImplicitSingleHostCIDR6, }, "dst_address_list": { Type: schema.TypeString, Optional: true, Description: "Matches destination address of a packet against user-defined address list.", }, "dst_address_type": { Type: schema.TypeString, Optional: true, Description: "Matches destination address type.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"unicast", "local", "broadcast", "multicast"}, false, true), }, "dst_limit": { Type: schema.TypeString, Optional: true, Description: "Matches packets until a given rate is exceeded.", }, "dst_port": { Type: schema.TypeString, Optional: true, Description: "List of destination port numbers or port number ranges.", }, KeyDynamic: PropDynamicRo, // fragment, hotspot. "icmp_options": { Type: schema.TypeString, Optional: true, Description: "Matches ICMP type: code fields.", }, "in_bridge_port": { Type: schema.TypeString, Optional: true, Description: "Actual interface the packet has entered the router if the incoming interface is a bridge. " + "Works only if use-ip-firewall is enabled in bridge settings.", }, "in_bridge_port_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as in-bridge-port.", }, "in_interface": { Type: schema.TypeString, Optional: true, Description: "Interface the packet has entered the router.", }, "in_interface_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as in-interface.", }, "ingress_priority": { Type: schema.TypeInt, Optional: true, Description: "Matches the priority of an ingress packet. Priority may be derived from VLAN, WMM, DSCP, " + "or MPLS EXP bit.", ValidateFunc: validation.IntBetween(0, 63), }, "invalid": { Type: schema.TypeBool, Computed: true, }, "ipsec_policy": { Type: schema.TypeString, Optional: true, Description: "Matches the policy used by IPsec. Value is written in the following format: direction, policy.", ValidateFunc: validation.StringMatch(regexp.MustCompile(`^(in|out)\s?,\s?(ipsec|none)$`), "Value must be written in the following format: direction, policy."), }, "jump_target": { Type: schema.TypeString, Optional: true, Description: "Name of the target chain to jump to. Applicable only if action=jump.", }, // No layer7 protocol "limit": { Type: schema.TypeString, Optional: true, Description: "Matches packets up to a limited rate (packet rate or bit rate). A rule using this matcher " + "will match until this limit is reached. Parameters are written in the following format: " + "rate[/time],burst:mode.", }, "log": { Type: schema.TypeBool, Optional: true, Description: "Add a message to the system log.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "log_prefix": { Type: schema.TypeString, Optional: true, Description: "Adds specified text at the beginning of every log message. Applicable if action=log or " + "log=yes configured.", }, "nth": { Type: schema.TypeString, Optional: true, Description: "Matches every nth packet: nth=2,1 rule will match every first packet of 2, hence, 50% of " + "all the traffic that is matched by the rule", }, "out_bridge_port": { Type: schema.TypeString, Optional: true, Description: "Actual interface the packet is leaving the router if the outgoing interface is a bridge. " + "Works only if use-ip-firewall is enabled in bridge settings.", }, "out_bridge_port_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as out-bridge-port.", }, "out_interface": { Type: schema.TypeString, Optional: true, Description: "Interface the packet is leaving the router.", }, "out_interface_list": { Type: schema.TypeString, Optional: true, Description: "Set of interfaces defined in interface list. Works the same as out-interface.", }, "packet_mark": { Type: schema.TypeString, Optional: true, Description: "Matches packets marked via mangle facility with particular packet mark. If no-mark is set, " + "the rule will match any unmarked packet.", }, "packet_size": { Type: schema.TypeString, Optional: true, Description: "Matches packets of specified size or size range in bytes.", }, KeyPlaceBefore: PropPlaceBefore, "port": { Type: schema.TypeString, Optional: true, Description: "Matches if any (source or destination) port matches the specified list of ports or port " + "ranges. Applicable only if protocol is TCP or UDP", }, "priority": { Type: schema.TypeInt, Optional: true, Description: "Matches the packet's priority after a new priority has been set. Priority may be derived from " + "VLAN, WMM, DSCP, MPLS EXP bit, or from the priority that has been set using the set-priority action.", ValidateFunc: validation.IntBetween(0, 63), }, "protocol": { Type: schema.TypeString, Optional: true, Description: "Matches particular IP protocol specified by protocol name or number.", }, // No psd "random": { Type: schema.TypeInt, Optional: true, Description: "Matches packets randomly with a given probability.", ValidateFunc: validation.IntBetween(1, 99), }, "randomise_ports": { Type: schema.TypeBool, Optional: true, Description: "Randomize to which public port connections will be mapped.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "routing_mark": { Type: schema.TypeString, Optional: true, Description: "Matches packets marked by mangle facility with particular routing mark.", }, "src_address": { Type: schema.TypeString, Optional: true, Description: "Matches packets which source is equal to specified IP or falls into a specified IP range.", DiffSuppressFunc: ImplicitSingleHostCIDR6, }, "src_address_list": { Type: schema.TypeString, Optional: true, Description: "Matches source address of a packet against user-defined address list.", }, "src_address_type": { Type: schema.TypeString, Optional: true, Description: "Matches source address type.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"unicast", "local", "broadcast", "multicast"}, false, true), }, "src_port": { Type: schema.TypeString, Optional: true, Description: "List of source ports and ranges of source ports. Applicable only if a protocol is TCP or UDP.", }, "src_mac_address": { Type: schema.TypeString, Optional: true, Description: "Matches source MAC address of the packet.", ValidateFunc: validation.IsMACAddress, }, "tcp_mss": { Type: schema.TypeString, Optional: true, Description: "Matches TCP MSS value of an IP packet.", }, "time": { Type: schema.TypeString, Optional: true, Description: "Allows to create a filter based on the packets' arrival time and date or, for locally " + "generated packets, departure time and date.", }, "to_address": { Type: schema.TypeString, Optional: true, Description: "Replace original address with specified one. Applicable if action is dst-nat, netmap, " + "same, src-nat.", }, "to_ports": { Type: schema.TypeString, Optional: true, Description: "Replace the original port with the specified one. Applicable if action is dst-nat, " + "redirect, masquerade, netmap, same, src-nat.", }, "ttl": { Type: schema.TypeString, Optional: true, Description: "Matches packets TTL value.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { skip := resSchema[MetaSkipFields].Default.(string) resSchema[MetaSkipFields].Default = skip + `,"place_before"` defer func() { resSchema[MetaSkipFields].Default = skip }() return ResourceUpdate(ctx, resSchema, d, m) }, DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ipv6_firewall_nat_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIPv6FirewallNat = "routeros_ipv6_firewall_nat.data" func TestAccIPv6FirewallNatTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ipv6/firewall/nat", "routeros_ipv6_firewall_nat"), Steps: []resource.TestStep{ { Config: testAccIPv6FirewallNatConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIPv6FirewallNat), resource.TestCheckResourceAttr(testIPv6FirewallNat, "chain", "srcnat"), resource.TestCheckResourceAttr(testIPv6FirewallNat, "action", "masquerade"), resource.TestCheckResourceAttr(testIPv6FirewallNat, "disabled", "true"), ), }, }, }) }) } } func testAccIPv6FirewallNatConfig() string { return providerConfig + ` resource "routeros_ipv6_firewall_nat" "data" { chain = "srcnat" action = "masquerade" disabled = true comment = "new-nat-rule" } ` } ================================================ FILE: routeros/resource_ipv6_nd_prefix.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*1", "6to4-interface": "none", "autonomous": "true", "disabled": "false", "interface": "lo", "invalid": "false", "on-link": "true", "preferred-lifetime": "1w", "prefix": "aaff::/64", "valid-lifetime": "4w2d" } */ // https://help.mikrotik.com/docs/spaces/ROS/pages/40992815/IPv6+Neighbor+Discovery#IPv6NeighborDiscovery-Prefix func ResourceIpv6NdPrefix() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ipv6/nd/prefix"), MetaId: PropId(Id), "6to4_interface": { Type: schema.TypeString, Optional: true, Description: "If this option is specified, this prefix will be combined with the IPv4 address of the interface " + "name to produce a valid 6to4 prefix. The first 16 bits of this prefix will be replaced by 2002 and the " + "next 32 bits of this prefix will be replaced by the IPv4 address assigned to the interface name at configuration " + "time. The remaining 80 bits of the prefix (including the SLA ID) will be advertised as specified in " + "the configuration file.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "autonomous": { Type: schema.TypeBool, Optional: true, Description: "When set, indicates that this prefix can be used for autonomous address configuration. Otherwise, " + "prefix information is silently ignored.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, KeyInterface: { Type: schema.TypeString, Required: true, Description: "Interface name on which stateless auto-configuration will be running.", }, KeyInvalid: PropInvalidRo, "on_link": { Type: schema.TypeBool, Optional: true, Description: "When set, indicates that this prefix can be used for on-link determination. When not set the " + "advertisement makes no statement about the on-link or off-link properties of the prefix. For instance, " + "the prefix might be used for address configuration with some of the addresses belonging to the prefix " + "being on-link and others being off-link.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "preferred_lifetime": { Type: schema.TypeString, Optional: true, Description: "Timeframe (relative to the time the packet is sent) after which generated address becomes " + "`deprecated`. Deprecated is used only for already existing connections and is usable until valid " + "lifetime expires.", DiffSuppressFunc: TimeEqual, }, "prefix": { Type: schema.TypeString, Required: true, Description: "A prefix from which stateless address autoconfiguration generates the valid address.", }, "valid_lifetime": { Type: schema.TypeString, Optional: true, Description: "The length of time (relative to the time the packet is sent) an address remains in the valid " + "state. The valid lifetime must be greater than or equal to the preferred lifetime.", DiffSuppressFunc: TimeEqual, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ipv6_nd_prefix_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpv6NdPrefix = "routeros_ipv6_nd_prefix.test" func TestAccIpv6NdPrefixTest_basic(t *testing.T) { t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ipv6/nd/prefix", "routeros_ipv6_nd_prefix"), Steps: []resource.TestStep{ { Config: testAccIpv6NdPrefixConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpv6NdPrefix), resource.TestCheckResourceAttr(testIpv6NdPrefix, "interface", "ether1"), resource.TestCheckResourceAttr(testIpv6NdPrefix, "prefix", "fd55::/64"), resource.TestCheckResourceAttr(testIpv6NdPrefix, "preferred_lifetime", "1w"), ), }, }, }) }) } } func testAccIpv6NdPrefixConfig() string { return fmt.Sprintf(`%v resource "routeros_ipv6_nd_prefix" "test" { prefix = "fd55::/64" interface = "ether1" preferred_lifetime = "6d24h" } `, providerConfig) } ================================================ FILE: routeros/resource_ipv6_neighbor_discovery.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* [ { ".id": "*3", "advertise-dns": "false", "advertise-mac-address": "true", "default": "false", "disabled": "false", "dns": "", "hop-limit": "unspecified", "interface": "vlan-wifi-XXX", "invalid": "false", "managed-address-configuration": "true", "mtu": "unspecified", "other-configuration": "true", "pref64": "", "ra-delay": "3s", "ra-interval": "3m20s-10m", "ra-lifetime": "30m", "ra-preference": "high", "reachable-time": "unspecified", "retransmit-interval": "unspecified" } ] */ // ResourceIPv6NeighborDiscovery https://help.mikrotik.com/docs/display/ROS/IPv6+Neighbor+Discovery func ResourceIPv6NeighborDiscovery() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ipv6/nd"), MetaId: PropId(Id), MetaDropByValue: PropDropByValue("unspecified"), "advertise_dns": { Type: schema.TypeBool, Optional: true, Description: "Option to redistribute DNS server information using RADVD. You will need a running client-side software with Router Advertisement DNS support to take advantage of the advertised DNS information.", Default: true, }, "advertise_mac_address": { Type: schema.TypeBool, Optional: true, Description: "When set, the link-layer address of the outgoing interface is included in the RA.", Default: true, }, KeyComment: PropCommentRw, KeyDefault: PropDefaultRo, "dns_servers": { Type: schema.TypeString, Optional: true, Description: "Specify a single IPv6 address or list of addresses that will be provided to hosts for DNS server configuration.", }, "dns": { Type: schema.TypeString, Optional: true, Description: "Specify a single IPv6 address or comma separated list of addresses that will be provided to hosts for DNS server configuration.", }, KeyDisabled: PropDisabledRw, "hop_limit": { Type: schema.TypeInt, Optional: true, Description: "The default value that should be placed in the Hop Count field of the IP header for outgoing (unicast) IP packets.", ValidateFunc: validation.IntBetween(0, 255), }, "interface": { Type: schema.TypeString, Required: true, Description: "The interface on which to run neighbor discovery." + "all - run ND on all running interfaces.", }, KeyInvalid: PropInvalidRo, "managed_address_configuration": { Type: schema.TypeBool, Optional: true, Description: "Name of the IPv6 pool in which received IPv6 prefix will be added", }, "mtu": { Type: schema.TypeInt, Optional: true, Description: "The flag indicates whether hosts should use stateful autoconfiguration (DHCPv6) to obtain addresses", ValidateFunc: validation.IntBetween(0, 90000), }, "other_configuration": { Type: schema.TypeBool, Optional: true, Description: "The flag indicates whether hosts should use stateful autoconfiguration to obtain additional information (excluding addresses).", }, "pref64": { Type: schema.TypeList, Optional: true, Description: "Specify IPv6 prefix or list of prefixes within /32, /40. /48, /56, /64, or /96 subnet that will be provided to hosts as NAT64 prefixes.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.IsCIDR, }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "ra_delay": { Type: schema.TypeString, Optional: true, Description: "The minimum time allowed between sending multicast router advertisements from the interface.", Default: "3s", }, "ra_interval": { Type: schema.TypeString, Optional: true, Description: "The min-max interval allowed between sending unsolicited multicast router advertisements from the interface.", Default: "3m20s-10m", }, "ra_preference": { Type: schema.TypeString, Optional: true, Description: "Specify the router preference that is communicated to IPv6 hosts through router advertisements." + "The preference value in the router advertisements enables IPv6 hosts to select a default router to reach a remote destination", Default: "medium", ValidateFunc: validation.StringInSlice([]string{"low", "medium", "high"}, false), }, "ra_lifetime": { Type: schema.TypeString, Optional: true, Description: "Specify the router preference that is communicated to IPv6 hosts through router advertisements." + "The preference value in the router advertisements enables IPv6 hosts to select a default router to reach a remote destination", Default: "30m", }, "reachable_time": { Type: schema.TypeString, Optional: true, Description: "Specify the router preference that is communicated to IPv6 hosts through router advertisements." + "The preference value in the router advertisements enables IPv6 hosts to select a default router to reach a remote destination", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "retransmit_interval": { Type: schema.TypeString, Optional: true, Description: "The time between retransmitted Neighbor Solicitation messages." + "Used by address resolution and the Neighbor Unreachability Detection algorithm (see Sections 7.2 and 7.3 of RFC 2461)", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ipv6_neighbor_discovery_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIPv6NeighborDiscoveryAddress = "routeros_ipv6_neighbor_discovery.test" func TestAccIPv6FNeighborDiscoveryTest_full(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ipv6/nd", "routeros_ipv6_neighbor_discovery"), Steps: []resource.TestStep{ { Config: testAccFullIPv6NeighborDiscoveryConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIPv6NeighborDiscoveryAddress), resource.TestCheckResourceAttr(testIPv6NeighborDiscoveryAddress, "interface", "ether1"), resource.TestCheckResourceAttr(testIPv6NeighborDiscoveryAddress, "hop_limit", "33"), resource.TestCheckResourceAttr(testIPv6NeighborDiscoveryAddress, "advertise_dns", "false"), resource.TestCheckResourceAttr(testIPv6NeighborDiscoveryAddress, "advertise_mac_address", "true"), resource.TestCheckResourceAttr(testIPv6NeighborDiscoveryAddress, "disabled", "false"), resource.TestCheckResourceAttr(testIPv6NeighborDiscoveryAddress, "managed_address_configuration", "true"), resource.TestCheckResourceAttr(testIPv6NeighborDiscoveryAddress, "mtu", "9000"), resource.TestCheckResourceAttr(testIPv6NeighborDiscoveryAddress, "other_configuration", "true"), resource.TestCheckResourceAttr(testIPv6NeighborDiscoveryAddress, "ra_delay", "3s"), resource.TestCheckResourceAttr(testIPv6NeighborDiscoveryAddress, "ra_interval", "3m20s-10m"), resource.TestCheckResourceAttr(testIPv6NeighborDiscoveryAddress, "ra_lifetime", "30m"), resource.TestCheckResourceAttr(testIPv6NeighborDiscoveryAddress, "ra_preference", "high"), resource.TestCheckResourceAttr(testIPv6NeighborDiscoveryAddress, "reachable_time", "10m"), resource.TestCheckResourceAttr(testIPv6NeighborDiscoveryAddress, "retransmit_interval", "12m"), ), }, }, }) }) } } func TestAccIPv6FNeighborDiscoveryTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ipv6/nd", "routeros_ipv6_neighbor_discovery"), Steps: []resource.TestStep{ { Config: testAccSimpleIPv6NeighborDiscoveryConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIPv6NeighborDiscoveryAddress), resource.TestCheckResourceAttr(testIPv6NeighborDiscoveryAddress, "interface", "ether1"), ), }, }, }) }) } } func testAccFullIPv6NeighborDiscoveryConfig() string { return providerConfig + ` resource "routeros_ipv6_neighbor_discovery" "test" { interface = "ether1" hop_limit = 33 advertise_dns = false advertise_mac_address = true disabled = false managed_address_configuration = true mtu = 9000 other_configuration = true pref64 = [] ra_delay = "3s" ra_interval = "3m20s-10m" ra_lifetime = "30m" ra_preference = "high" reachable_time = "10m" retransmit_interval = "12m" } ` } func testAccSimpleIPv6NeighborDiscoveryConfig() string { return providerConfig + ` resource "routeros_ipv6_neighbor_discovery" "test" { interface = "ether1" } ` } ================================================ FILE: routeros/resource_ipv6_pool.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*2", "dynamic": "false", "name": "test-pool", "prefix": "2001:db8:12::/48", "prefix-length": "64" } */ // https://help.mikrotik.com/docs/display/ROS/IP+Pools#IPPools-IPv6Pool func ResourceIpv6Pool() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ipv6/pool"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("expire_time"), KeyDynamic: PropDynamicRo, KeyName: PropName("Descriptive name of the pool."), "prefix": { Type: schema.TypeString, Optional: true, Description: "Ipv6 address prefix.", ValidateFunc: validation.IsCIDR, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "prefix_length": { Type: schema.TypeInt, Required: true, Description: "The option represents the prefix size that will be given out to the client.", ValidateFunc: validation.IntBetween(1, 128), }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ipv6_pool_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpv6Pool = "routeros_ipv6_pool.test" func TestAccIpv6PoolTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ipv6/pool", "routeros_ipv6_pool"), Steps: []resource.TestStep{ { Config: testAccIpv6PoolConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpv6Pool), resource.TestCheckResourceAttr(testIpv6Pool, "name", "test-pool"), resource.TestCheckResourceAttr(testIpv6Pool, "prefix", "2001:db8:12::/48"), resource.TestCheckResourceAttr(testIpv6Pool, "prefix_length", "64"), ), }, }, }) }) } } func testAccIpv6PoolConfig() string { return fmt.Sprintf(`%v resource "routeros_ipv6_pool" "test" { name = "test-pool" prefix = "2001:db8:12::/48" prefix_length = 64 } `, providerConfig) } ================================================ FILE: routeros/resource_ipv6_route.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) // ResourceIPv6Route https://help.mikrotik.com/docs/display/ROS/IP+Routing func ResourceIPv6Route() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ipv6/route"), MetaId: PropId(Id), "active": { Type: schema.TypeBool, Computed: true, Description: "A flag indicates whether the route is elected as Active and eligible to be added to the FIB.", }, // If the parameter is present in the request, the MT automatically marks the route as a blackhole. // To solve this problem, let's remove the default value and the parameter will be present in the query // only when explicitly specified in the configuration. "blackhole": { Type: schema.TypeBool, Optional: true, Description: "It's a blackhole route. If you need to cancel route marking, then simply delete the " + "parameter from the configuration of the TF. The value of the parameter (true or false) has no " + "effect on the MT processing logic.", }, "check_gateway": { Type: schema.TypeString, Optional: true, Description: "Currently used check-gateway option.", ValidateFunc: validation.StringInSlice([]string{"arp", "bfd", "ping"}, false), }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "distance": { Type: schema.TypeInt, Optional: true, Default: 1, Description: "Value used in route selection. Routes with smaller distance value are given preference.", ValidateFunc: validation.IntBetween(1, 255), }, "dst_address": { Type: schema.TypeString, Required: true, Description: "IP prefix of route, specifies destination addresses that this route can be used for.", ValidateFunc: validation.IsCIDR, }, KeyDynamic: PropDynamicRo, "ecmp": { Type: schema.TypeBool, Computed: true, Description: "A flag indicates whether the route is added as an Equal-Cost Multi-Path route in the FIB.", }, "gateway": { Type: schema.TypeString, Required: true, Description: "Array of IP addresses or interface names. Specifies which host or interface packets should " + "be sent to (IP | interface | IP%interface | IP@table[, IP | string, [..]]).", }, KeyHwOffloaded: PropHwOffloadedRo, "immediate_gw": { Type: schema.TypeString, Computed: true, Description: "Shows actual (resolved) gateway and interface that will be used for packet forwarding.", }, KeyInactive: PropInactiveRo, "pref_src": { Type: schema.TypeString, Optional: true, Description: "Which of the local IP addresses to use for locally originated packets that are sent via this " + "route. Value of this property has no effect on forwarded packets. If value of this property is set " + "to IP address that is not local address of this router then the route will be inactive (in ROS v6, " + "ROS v7 allows IP spoofing).", }, "routing_table": { Type: schema.TypeString, Optional: true, Default: "main", Description: "Routing table this route belongs to.", }, "scope": { Type: schema.TypeInt, Optional: true, Default: 30, Description: "Used in nexthop resolution. Route can resolve nexthop only through routes that have scope " + "less than or equal to the target-scope of this route.", ValidateFunc: validation.IntBetween(0, 255), }, "static": { Type: schema.TypeBool, Computed: true, }, "suppress_hw_offload": { Type: schema.TypeBool, Optional: true, }, "target_scope": { Type: schema.TypeInt, Optional: true, Default: 10, Description: "Used in nexthop resolution. This is the maximum value of scope for a route through which a " + "nexthop of this route can be resolved.", ValidateFunc: validation.IntBetween(0, 255), }, "vrf_interface": { Type: schema.TypeString, Computed: true, Optional: true, Description: "VRF interface name.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ipv6_route_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIPv6RouteAddress = "routeros_ipv6_route.test_route" func TestAccIPv6RouteTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ipv6/route", "routeros_ipv6_route"), Steps: []resource.TestStep{ { Config: testAccIPv6RouteConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIPv6RouteAddress), resource.TestCheckResourceAttr(testIPv6RouteAddress, "distance", "1"), resource.TestCheckResourceAttr(testIPv6RouteAddress, "dst_address", "::/0"), resource.TestCheckResourceAttr(testIPv6RouteAddress, "gateway", "2001:db8:1000::1"), ), }, }, }) }) } } func testAccIPv6RouteConfig() string { return providerConfig + ` resource "routeros_ipv6_route" "test_route" { distance = 1 dst_address = "::/0" gateway = "2001:db8:1000::1" } ` } ================================================ FILE: routeros/resource_ipv6_settings.go ================================================ package routeros import ( "regexp" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { "accept-redirects": "yes-if-forwarding-disabled", "accept-router-advertisements": "yes-if-forwarding-disabled", "allow-fast-path": "true", "disable-ipv6": "false", "disable-link-local-address": "false", "forward": "true", "ipv6-fast-path-active": "true", "ipv6-fast-path-bytes": "0", "ipv6-fast-path-packets": "0", "ipv6-fasttrack-active": "false", "ipv6-fasttrack-bytes": "0", "ipv6-fasttrack-packets": "0", "max-neighbor-entries": "16384", "min-neighbor-entries": "4096", "multipath-hash-policy": "l3", "soft-max-neighbor-entries": "8192", "stale-neighbor-detect-interval": "30", "stale-neighbor-timeout": "60" } */ // https://help.mikrotik.com/docs/spaces/ROS/pages/103841817/IP+Settings#IPSettings-IPv6Settings func ResourceIpv6Settings() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ipv6/settings"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("ipv6_fast_path_bytes", "ipv6_fast_path_packets", "ipv6_fasttrack_bytes", "ipv6_fasttrack_packets"), "allow_fast_path": { Type: schema.TypeBool, Optional: true, Description: "Allows Fast Path.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "accept_redirects": { Type: schema.TypeString, Optional: true, Description: "Whether to accept ICMP redirect messages. Typically should be enabled on the host and disabled " + "on routers.", ValidateFunc: validation.StringInSlice([]string{"no", "yes-if-forwarding-disabled"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "accept_router_advertisements": { Type: schema.TypeString, Optional: true, Description: "Accept router advertisement (RA) messages. If enabled, the router will be able to get the " + "address using stateless address configuration.", ValidateFunc: validation.StringInSlice([]string{"no", "yes", "yes-if-forwarding-disabled"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "accept_router_advertisements_on": { Type: schema.TypeString, Optional: true, Description: "Accept router advertisement (RA) messages. If enabled, the router will be able to get the " + "address using stateless address configuration on specified or specific interfaces", ValidateFunc: validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9:._-]{2,255}$`), "must be 2 to 255 characters and contain only letters, numbers, or :._-"), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "disable_ipv6": { Type: schema.TypeBool, Optional: true, Description: "Enable/disable system wide IPv6 settings (prevents LL address generation).", }, "disable_link_local_address": { Type: schema.TypeBool, Optional: true, Description: "Disable automatic link-local address generation for non-VPN interfaces. This can be used when " + "manually configured link-local addresses are being used.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "forward": { Type: schema.TypeBool, Optional: true, Description: "Enable/disable packet forwarding between interfaces.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "ipv6_fast_path_active": { Type: schema.TypeBool, Computed: true, Description: "Indicates whether fast-path is active.", }, "ipv6_fasttrack_active": { Type: schema.TypeBool, Computed: true, Description: "Indicates whether fasttrack is active.", }, "max_neighbor_entries": { Type: schema.TypeInt, Optional: true, Description: "A maximum number or IPv6 neighbors. Since RouterOS version 7.1, the default value depends " + "on the installed amount of RAM. It is possible to set a higher value than the default, but it increases " + "the risk of out-of-memory condition. The default values for certain RAM sizes:\n * 1024 for 64 MB,\n * 2048 " + "for 128 MB,\n * 4096 for 256 MB,\n * 8192 for 512 MB,\n * 16384 for 1024 MB or higher.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "min_neighbor_entries": { Type: schema.TypeInt, Optional: true, Description: "Minimal number of IPv6/Neighbor entries, for which device must allocate memory.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "multipath_hash_policy": { Type: schema.TypeString, Optional: true, Description: "IPv6 Hash policy used for ECMP routing in `/ipv6/settings` menu\n * l3 -- layer-3 hashing of src " + "IP, dst IP, flow label, IP protocol\n * l3-inner -- layer-3 hashing or inner layer-3 hashing if available" + "\n * l4 -- layer-4 hashing of src IP, dst IP, IP protocol, src port, dst port.", ValidateFunc: validation.StringInSlice([]string{"l3", "l4", "l3-inner"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "soft_max_neighbor_entries": { Type: schema.TypeInt, Optional: true, Description: "Expected maximum number of IPv6/Neighbor entries which system should handle.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "stale_neighbor_detect_interval": { Type: schema.TypeString, Optional: true, Description: "", DiffSuppressFunc: TimeEqual, }, "stale_neighbor_timeout": { Type: schema.TypeString, Optional: true, Description: "Timeout after which stale IPv6/Neighbor entries should be purged.", DiffSuppressFunc: TimeEqual, }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ipv6_settings_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testIpv6Settings = "routeros_ipv6_settings.settings" func TestAccIpv6SettingsTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccIpv6SettingsConfig("no"), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpv6Settings), resource.TestCheckResourceAttr(testIpv6Settings, "accept_redirects", "no"), ), }, { Config: testAccIpv6SettingsConfig("yes-if-forwarding-disabled"), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testIpv6Settings), resource.TestCheckResourceAttr(testIpv6Settings, "accept_redirects", "yes-if-forwarding-disabled"), ), }, }, }) }) } } func testAccIpv6SettingsConfig(param string) string { return fmt.Sprintf(`%v resource "routeros_ipv6_settings" "settings" { accept_redirects = "%v" } `, providerConfig, param) } ================================================ FILE: routeros/resource_move.go ================================================ package routeros import ( "context" "fmt" "regexp" "strings" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) func ResourceMoveItems() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/path"), MetaId: PropId(Id), "resource_name": { Type: schema.TypeString, Optional: true, Description: "Resource name in the notation ```routeros_ip_firewall_filter```.", ValidateFunc: validation.StringMatch(regexp.MustCompile(`^routeros(_\w+)+$`), ""), AtLeastOneOf: []string{"resource_name", "resource_path"}, }, "resource_path": { Type: schema.TypeString, Optional: true, Description: "URL path of the resource in the notation ```/ip/firewall/filter```.", ValidateFunc: validation.StringMatch(regexp.MustCompile(`^(/\w+)+$`), ""), AtLeastOneOf: []string{"resource_name", "resource_path"}, }, "sequence": { Type: schema.TypeList, Required: true, Description: "List identifiers in the required sequence. To locate the ```sequence``` before an " + "existing rule, add its ```id``` to the last element of the ```sequence```.", Elem: &schema.Schema{ Type: schema.TypeString, MinItems: 2, }, }, // "anchor_rule": { // Type: schema.TypeString, // Optional: true, // Description: "The rule before which the ```sequence``` of rules will be placed. If this field is not specified, " + // "the rules will be placed before the last element of the ```sequence```.", // }, } resRead := func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { path, ok := d.GetOk("resource_path") if !ok { path = d.Get("resource_name") path = strings.TrimPrefix(path.(string), "routeros_") path = strings.ReplaceAll(path.(string), "_", "/") } res, err := ReadItems(nil, path.(string), m.(Client)) if err != nil { ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgGet, err)) return diag.FromErr(err) } // Resource not found. if len(*res) == 0 { d.SetId("") return nil } var conf = make(map[string]struct{}) for _, v := range d.Get("sequence").([]any) { conf[v.(string)] = struct{}{} } // TODO It is necessary to sometime transfer the logic for monotonous reading of the sequence, so that it is possible // to control the correctness of the position of the rules and possibly introduce an anchor rule. // This check will not work in all cases!!! var list []string for _, r := range *res { if id, ok := r[".id"]; ok { if _, ok := conf[id]; ok { list = append(list, id) } } } d.Set("sequence", list) return nil } resCreateUpdate := func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { var list []string for _, v := range d.Get("sequence").([]any) { list = append(list, v.(string)) } // if anchor, ok := d.GetOk("anchor_rule"); ok { // item = MikrotikItem{ // "numbers": strings.Join(list, ","), // "destination": anchor.(string), // } // } else { var item MikrotikItem = MikrotikItem{ "numbers": strings.Join(list[:len(list)-1], ","), "destination": list[len(list)-1], } // } path, ok := d.GetOk("resource_path") if !ok { path = d.Get("resource_name") path = strings.TrimPrefix(path.(string), "routeros_") path = strings.ReplaceAll(path.(string), "_", "/") } if m.(Client).GetTransport() == TransportREST { path = path.(string) + "/move" } err := m.(Client).SendRequest(crudMove, &URL{Path: path.(string)}, item, nil) if err != nil { ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgPut, err)) return diag.FromErr(err) } d.SetId(strings.ReplaceAll(strings.TrimLeft(path.(string), "/"), "/", ".")) return resRead(ctx, d, m) } return &schema.Resource{ CreateContext: resCreateUpdate, ReadContext: resRead, UpdateContext: resCreateUpdate, DeleteContext: DefaultSystemDelete(resSchema), Schema: resSchema, } } ================================================ FILE: routeros/resource_ovpn_client.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*5C", "add-default-route": "false", "auth": "sha1", "certificate": "none", "cipher": "blowfish128", * "connect-to": "192.168.1.1", "disabled": "false", "hw-crypto": "false", "mac-address": "02:E7:60:C6:40:EE", "max-mtu": "1500", "mode": "ip", "name": "ovpn-out1", "password": "", "port": "1194", "profile": "default", "protocol": "tcp", "route-nopull": "false", "running": "false", "tls-version": "any", "use-peer-dns": "yes", * "user": "aaa", "verify-server-certificate": "false" } */ // https://help.mikrotik.com/docs/display/ROS/OpenVPN func ResourceOpenVPNClient() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/ovpn-client"), MetaId: PropId(Id), "add_default_route": { Type: schema.TypeBool, Optional: true, Description: "Whether to add OVPN remote address as a default route.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "auth": { Type: schema.TypeString, Optional: true, Description: "Authentication methods that the server will accept.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateDiagFunc: ValidationMultiValInSlice([]string{"md5", "sha1", "null", "sha256", "sha512"}, false, false), }, "certificate": { Type: schema.TypeString, Optional: true, Description: "Name of the client certificate.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "cipher": { Type: schema.TypeString, Optional: true, Description: `Allowed ciphers.`, DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateDiagFunc: ValidationMultiValInSlice([]string{ "null", "aes128-cbc", "aes128-gcm", "aes192-cbc", "aes192-gcm", "aes256-cbc", "aes256-gcm", "blowfish128", // Backward compatibility with ROS v7.7 "aes128", "aes192", "aes256", }, false, false), }, KeyComment: PropCommentRw, "connect_to": { Type: schema.TypeString, Required: true, Description: "Remote address of the OVPN server.", }, KeyDisabled: PropDisabledRw, "hw_crypto": { Type: schema.TypeBool, Computed: true, Description: "", }, KeyMacAddress: PropMacAddressRw(`Mac address of OVPN interface. Will be automatically generated if not specified.`, false), "max_mtu": { Type: schema.TypeInt, Optional: true, Description: "Maximum Transmission Unit. Max packet size that the OVPN interface will be able to send without packet fragmentation.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.IntBetween(64, 65535), }, "mode": { Type: schema.TypeString, Optional: true, Description: "Layer3 or layer2 tunnel mode (alternatively tun, tap)", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.StringInSlice([]string{"ip", "ethernet"}, false), }, KeyName: PropName("Descriptive name of the interface."), "password": { Type: schema.TypeString, Optional: true, Description: "Password used for authentication.", Sensitive: true, }, "port": { Type: schema.TypeInt, Optional: true, Description: "Port to connect to.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.IntBetween(1, 65535), }, "profile": { Type: schema.TypeString, Optional: true, Description: "Specifies which PPP profile configuration will be used when establishing the tunnel.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "protocol": { Type: schema.TypeString, Optional: true, Description: "Indicates the protocol to use when connecting with the remote endpoint.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.StringInSlice([]string{"tcp", "udp"}, false), }, "route_nopull": { Type: schema.TypeBool, Optional: true, Description: "Specifies whether to allow the OVPN server to add routes to the OVPN client instance " + "routing table.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyRunning: PropRunningRo, "tls_version": { Type: schema.TypeString, Optional: true, Description: "Specifies which TLS versions to allow.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.StringInSlice([]string{"any", "only-1.2"}, false), }, "use_peer_dns": { Type: schema.TypeBool, Optional: true, Description: "Whether to add DNS servers provided by the OVPN server to IP/DNS configuration.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "user": { Type: schema.TypeString, Required: true, Description: "User name used for authentication.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "verify_server_certificate": { Type: schema.TypeBool, Optional: true, Description: `Checks the certificates CN or SAN against the "connect-to" parameter. The IP or ` + `hostname must be present in the server's certificate.`, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ovpn_client_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testInterfaceOpenVPNClient = "routeros_interface_ovpn_client.ovpn-in1" func TestAccOpenVPNClientTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccOpenVPNClientConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testInterfaceOpenVPNClient), resource.TestCheckResourceAttr(testInterfaceOpenVPNClient, "name", "ovpn-in1"), resource.TestCheckResourceAttr(testInterfaceOpenVPNClient, "user", "user1"), resource.TestCheckResourceAttr(testInterfaceOpenVPNClient, "connect_to", "192.168.1.1"), ), }, }, }) }) } } // Complex test for OpenVPN client resources. func testAccOpenVPNClientConfig() string { return providerConfig + ` resource "routeros_system_certificate" "ovpn_ca" { name = "OpenVPN-Root-CA" common_name = "OpenVPN Root CA" key_size = "prime256v1" key_usage = ["key-cert-sign", "crl-sign"] trusted = true sign { } } resource "routeros_system_certificate" "ovpn_client_crt" { name = "OpenVPN-Client-Certificate" common_name = "Mikrotik OpenVPN Client" key_size = "prime256v1" key_usage = ["digital-signature", "key-encipherment", "tls-client"] sign { ca = routeros_system_certificate.ovpn_ca.name } } resource "routeros_interface_ovpn_client" "ovpn-in1" { name = "ovpn-in1" user = "user1" connect_to = "192.168.1.1" certificate = routeros_system_certificate.ovpn_client_crt.name } ` } ================================================ FILE: routeros/resource_ovpn_server.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { "auth": "sha1,md5,sha256,sha512", "certificate": "root-cert", "cipher": "blowfish128,aes128-cbc", "default-profile": "default", "enable-tun-ipv6": "false", "enabled": "true", "ipv6-prefix-len": "64", "keepalive-timeout": "60", "mac-address": "FE:01:63:24:35:19", "max-mtu": "1500", "mode": "ip", "netmask": "24", "port": "1194", "protocol": "tcp", "redirect-gateway": "disabled", "reneg-sec": "3600", "require-client-certificate": "true", "tls-version": "only-1.2", "tun-server-ipv6": "::" } */ // https://help.mikrotik.com/docs/display/ROS/OpenVPN func ResourceOpenVPNServer() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/ovpn-server/server"), MetaId: PropId(Id), "auth": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"md5", "sha1", "null", "sha256", "sha512"}, false), }, Description: "Authentication methods that the server will accept.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "certificate": { Type: schema.TypeString, Optional: true, Description: "Name of the certificate that the OVPN server will use.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "cipher": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{ "null", "aes128-cbc", "aes128-gcm", "aes192-cbc", "aes192-gcm", "aes256-cbc", "aes256-gcm", "blowfish128", // Backward compatibility with ROS v7.7 "aes128", "aes192", "aes256", }, false), }, Description: `Allowed ciphers.`, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "default_profile": { Type: schema.TypeString, Optional: true, Default: "default", Description: "Default profile to use.", }, "enable_tun_ipv6": { Type: schema.TypeBool, Optional: true, Default: false, Description: "Specifies if IPv6 IP tunneling mode should be possible with this OVPN server.", }, KeyEnabled: PropEnabled("Defines whether the OVPN server is enabled or not."), "ipv6_prefix_len": { Type: schema.TypeInt, Optional: true, Default: 64, Description: "Length of IPv6 prefix for IPv6 address which will be used when generating OVPN interface " + "on the server side.", ValidateFunc: validation.IntBetween(1, 128), }, "keepalive_timeout": { Type: schema.TypeString, Optional: true, Default: "60", Description: "Defines the time period (in seconds) after which the router is starting to send " + "keepalive packets every second. If no traffic and no keepalive responses have come for " + "that period of time (i.e. 2 * keepalive-timeout), not responding client is proclaimed " + "disconnected", DiffSuppressFunc: TimeEqual, }, // Computed only??? "mac_address": { Type: schema.TypeString, Optional: true, Computed: true, Description: "Automatically generated MAC address of the server.", }, "max_mtu": { Type: schema.TypeInt, Optional: true, Default: 1500, Description: "Maximum Transmission Unit. Max packet size that the OVPN interface will be able to send " + "without packet fragmentation.", ValidateFunc: validation.IntBetween(64, 65535), }, "mode": { Type: schema.TypeString, Optional: true, Default: "ip", Description: "Layer3 or layer2 tunnel mode (alternatively tun, tap)", ValidateFunc: validation.StringInSlice([]string{"ip", "ethernet"}, false), }, "netmask": { Type: schema.TypeInt, Optional: true, Default: 24, Description: "Subnet mask to be applied to the client.", ValidateFunc: validation.IntBetween(0, 32), }, "port": { Type: schema.TypeInt, Optional: true, Default: 1194, Description: "Port to run the server on.", ValidateFunc: validation.IntBetween(1, 65535), }, "protocol": { Type: schema.TypeString, Optional: true, Default: "tcp", Description: "indicates the protocol to use when connecting with the remote endpoint.", ValidateFunc: validation.StringInSlice([]string{"tcp", "udp"}, false), }, "push_routes": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "Push routes to the VPN client (available since RouterOS 7.14).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "redirect_gateway": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"def1", "disabled", "ipv6"}, false), }, Description: "Specifies what kind of routes the OVPN client must add to the routing table.\n * def1 – Use " + "this flag to override the default gateway by using 0.0.0.0/1 and 128.0.0.0/1 rather " + "than 0.0.0.0/0. This has the benefit of overriding but not wiping out the original " + "default gateway.\n * disabled - Do not send redirect-gateway flags to the OVPN client.\n * ipv6 " + "- Redirect IPv6 routing into the tunnel on the client side. This works similarly to the " + "def1 flag, that is, more specific IPv6 routes are added (2000::/4 and 3000::/4), " + "covering the whole IPv6 unicast space.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "reneg_sec": { Type: schema.TypeInt, Optional: true, Default: 3600, Description: "Renegotiate data channel key after n seconds (default=3600).", }, "require_client_certificate": { Type: schema.TypeBool, Optional: true, Default: false, Description: "If set to yes, then the server checks whether the client's certificate belongs to the " + "same certificate chain.", }, "tls_version": { Type: schema.TypeString, Optional: true, Default: "any", Description: "Specifies which TLS versions to allow.", ValidateFunc: validation.StringInSlice([]string{"any", "only-1.2"}, false), }, "tun_server_ipv6": { Type: schema.TypeString, Optional: true, Default: "::", Description: "IPv6 prefix address which will be used when generating the OVPN interface on the server " + "side.", }, } return &schema.Resource{ Description: `##### *This resource requires a minimum version of RouterOS 7.8!*`, CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, SchemaVersion: 1, StateUpgraders: []schema.StateUpgrader{ { Type: ResourceOpenVPNServerV0().CoreConfigSchema().ImpliedType(), Upgrade: stateMigrationScalarToList("auth", "cipher", "redirect_gateway"), Version: 0, }, }, } } ================================================ FILE: routeros/resource_ovpn_server_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testOpenVPNServerMinVersion = "7.8" const testOpenVPNServer = "routeros_ovpn_server.server" const testInterfaceOpenVPNServer = "routeros_interface_ovpn_server.user1" func TestAccOpenVPNServerTest_basic(t *testing.T) { if !testCheckMinVersion(t, testOpenVPNServerMinVersion) { t.Logf("Test skipped, the minimum required version is %v", testOpenVPNServerMinVersion) return } for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccOpenVPNServerConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testOpenVPNServer), testResourcePrimaryInstanceId(testInterfaceOpenVPNServer), resource.TestCheckResourceAttr(testOpenVPNServer, "id", "interface.ovpn-server.server"), resource.TestCheckResourceAttr(testInterfaceOpenVPNServer, "name", "ovpn-in1"), resource.TestCheckResourceAttr(testInterfaceOpenVPNServer, "user", "user1"), ), }, }, }) }) } } // Complex test for OpenVPN server resources. func testAccOpenVPNServerConfig() string { return providerConfig + ` resource "routeros_system_certificate" "ovpn_ca" { name = "OpenVPN-Root-CA" common_name = "OpenVPN Root CA" key_size = "prime256v1" key_usage = ["key-cert-sign", "crl-sign"] trusted = true sign { } } resource "routeros_system_certificate" "ovpn_server_crt" { name = "OpenVPN-Server-Certificate" common_name = "Mikrotik OpenVPN" key_size = "prime256v1" key_usage = ["digital-signature", "key-encipherment", "tls-server"] sign { ca = routeros_system_certificate.ovpn_ca.name } } resource "routeros_ovpn_server" "server" { enabled = false certificate = routeros_system_certificate.ovpn_server_crt.name auth = ["sha256", "sha512"] redirect_gateway = ["def1", "ipv6"] tls_version = "only-1.2" } # The resource should be created only after the OpenVPN server is enabled! resource "routeros_interface_ovpn_server" "user1" { name = "ovpn-in1" user = "user1" depends_on = [routeros_ovpn_server.server] } ` } ================================================ FILE: routeros/resource_ovpn_server_v0.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { "auth": "sha1,md5,sha256,sha512", "certificate": "root-cert", "cipher": "blowfish128,aes128-cbc", "default-profile": "default", "enable-tun-ipv6": "false", "enabled": "true", "ipv6-prefix-len": "64", "keepalive-timeout": "60", "mac-address": "FE:01:63:24:35:19", "max-mtu": "1500", "mode": "ip", "netmask": "24", "port": "1194", "protocol": "tcp", "redirect-gateway": "disabled", "reneg-sec": "3600", "require-client-certificate": "true", "tls-version": "only-1.2", "tun-server-ipv6": "::" } */ // https://help.mikrotik.com/docs/display/ROS/OpenVPN func ResourceOpenVPNServerV0() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/ovpn-server/server"), MetaId: PropId(Id), "auth": { Type: schema.TypeString, Optional: true, Default: "sha1,md5,sha256,sha512", Description: "Authentication methods that the server will accept.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"md5", "sha1", "null", "sha256", "sha512"}, false, false), }, "certificate": { Type: schema.TypeString, Required: true, Description: "Name of the certificate that the OVPN server will use.", }, "cipher": { Type: schema.TypeString, Optional: true, Default: "blowfish128,aes128-cbc", Description: `Allowed ciphers.`, ValidateDiagFunc: ValidationMultiValInSlice([]string{ "null", "aes128-cbc", "aes128-gcm", "aes192-cbc", "aes192-gcm", "aes256-cbc", "aes256-gcm", "blowfish128", // Backward compatibility with ROS v7.7 "aes128", "aes192", "aes256", }, false, false), }, "default_profile": { Type: schema.TypeString, Optional: true, Default: "default", Description: "Default profile to use.", }, "enable_tun_ipv6": { Type: schema.TypeBool, Optional: true, Description: "Specifies if IPv6 IP tunneling mode should be possible with this OVPN server.", }, KeyEnabled: PropEnabled("Defines whether the OVPN server is enabled or not."), "ipv6_prefix_len": { Type: schema.TypeInt, Optional: true, Default: 64, Description: "Length of IPv6 prefix for IPv6 address which will be used when generating OVPN interface " + "on the server side.", ValidateFunc: validation.IntBetween(1, 128), }, "keepalive_timeout": { Type: schema.TypeString, Optional: true, Default: "60", Description: "Defines the time period (in seconds) after which the router is starting to send " + "keepalive packets every second. If no traffic and no keepalive responses have come for " + "that period of time (i.e. 2 * keepalive-timeout), not responding client is proclaimed " + "disconnected", DiffSuppressFunc: TimeEqual, }, // Computed only??? "mac_address": { Type: schema.TypeString, Optional: true, Computed: true, Description: "Automatically generated MAC address of the server.", }, "max_mtu": { Type: schema.TypeInt, Optional: true, Default: 1500, Description: "Maximum Transmission Unit. Max packet size that the OVPN interface will be able to send " + "without packet fragmentation.", ValidateFunc: validation.IntBetween(64, 65535), }, "mode": { Type: schema.TypeString, Optional: true, Default: "ip", Description: "Layer3 or layer2 tunnel mode (alternatively tun, tap)", ValidateFunc: validation.StringInSlice([]string{"ip", "ethernet"}, false), }, "netmask": { Type: schema.TypeInt, Optional: true, Default: 24, Description: "Subnet mask to be applied to the client.", ValidateFunc: validation.IntBetween(0, 32), }, "port": { Type: schema.TypeInt, Optional: true, Default: 1194, Description: "Port to run the server on.", ValidateFunc: validation.IntBetween(1, 65535), }, "protocol": { Type: schema.TypeString, Optional: true, Default: "tcp", Description: "indicates the protocol to use when connecting with the remote endpoint.", ValidateFunc: validation.StringInSlice([]string{"tcp", "udp"}, false), }, "push_routes": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "Push routes to the VPN client (available since RouterOS 7.14).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "redirect_gateway": { Type: schema.TypeString, Optional: true, Default: "", Description: "Specifies what kind of routes the OVPN client must add to the routing table. def1 – Use " + "this flag to override the default gateway by using 0.0.0.0/1 and 128.0.0.0/1 rather " + "than 0.0.0.0/0. This has the benefit of overriding but not wiping out the original " + "default gateway. disabled - Do not send redirect-gateway flags to the OVPN client. ipv6 " + "- Redirect IPv6 routing into the tunnel on the client side. This works similarly to the " + "def1 flag, that is, more specific IPv6 routes are added (2000::/4 and 3000::/4), " + "covering the whole IPv6 unicast space.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"def1", "disabled", "ipv6"}, false, false), }, "reneg_sec": { Type: schema.TypeInt, Optional: true, Default: 3600, Description: "Renegotiate data channel key after n seconds (default=3600).", }, "require_client_certificate": { Type: schema.TypeBool, Optional: true, Description: "If set to yes, then the server checks whether the client's certificate belongs to the " + "same certificate chain.", }, "tls_version": { Type: schema.TypeString, Optional: true, Description: "Specifies which TLS versions to allow.", ValidateFunc: validation.StringInSlice([]string{"any", "only-1.2"}, false), }, "tun_server_ipv6": { Type: schema.TypeString, Optional: true, Default: "::", Description: "IPv6 prefix address which will be used when generating the OVPN interface on the server " + "side.", }, } return &schema.Resource{ Description: `##### *This resource requires a minimum version of RouterOS 7.8!*`, CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ppp_aaa.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { "accounting": "true", "interim-update": "0s", "use-circuit-id-in-nas-port-id": "false", "use-radius": "false" } */ // https://help.mikrotik.com/docs/display/ROS/PPP+AAA func ResourcePppAaa() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ppp/aaa"), MetaId: PropId(Id), "accounting": { Type: schema.TypeBool, Optional: true, Default: true, Description: "An option that enables accounting for users.", }, "enable_ipv6_accounting": { Type: schema.TypeBool, Optional: true, Description: "An option that enables IPv6 separate accounting.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "interim_update": { Type: schema.TypeString, Optional: true, Default: "0s", Description: "Interval between scheduled RADIUS Interim-Update messages.", DiffSuppressFunc: TimeEqual, }, "use_circuit_id_in_nas_port_id": { Type: schema.TypeBool, Optional: true, Default: false, }, "use_radius": { Type: schema.TypeBool, Optional: true, Default: false, Description: "An option whether to use RADIUS server.", }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ppp_aaa_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testPppAaaAddress = "routeros_ppp_aaa.test" func TestAccPppAaaTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccPppAaaConfig(0), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testPppAaaAddress), resource.TestCheckResourceAttr(testPppAaaAddress, "accounting", "true"), resource.TestCheckResourceAttr(testPppAaaAddress, "use_radius", "true"), ), }, { Config: testAccPppAaaConfig(1), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testPppAaaAddress), resource.TestCheckResourceAttr(testPppAaaAddress, "accounting", "false"), resource.TestCheckResourceAttr(testPppAaaAddress, "use_radius", "false"), ), }, }, }) }) } } func testAccPppAaaConfig(n int) string { var conf = []string{ ` resource "routeros_ppp_aaa" "test" { accounting = true use_radius = true }`, ` resource "routeros_ppp_aaa" "test" { accounting = false use_radius = false }`, } return providerConfig + conf[n] } ================================================ FILE: routeros/resource_ppp_profile.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "address-list": "", "bridge-learning": "default", "change-tcp-mss": "default", "default": "false", "insert-queue-before": "first", "local-address": "192.168.77.1", "name": "ovpn", "on-down": "", "on-up": "", "only-one": "default", "parent-queue": "none", "queue-type": "multi-queue-ethernet-default", "remote-address": "*2", "use-compression": "default", "use-encryption": "default", "use-ipv6": "yes", "use-mpls": "default", "use-upnp": "default" } */ // https://help.mikrotik.com/docs/display/ROS/PPP+AAA#PPPAAA-UserProfiles // https://help.mikrotik.com/docs/spaces/ROS/pages/132350049/PPP+AAA func ResourcePPPProfile() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ppp/profile"), MetaId: PropId(Id), "address_list": { Type: schema.TypeString, Optional: true, Description: "Address list name to which ppp assigned (on server) or received (on client) address will " + "be added.", }, "bridge": { Type: schema.TypeString, Optional: true, Description: "Name of the bridge interface to which ppp interface will be added as a slave port. Both " + "tunnel endpoints (server and client) must be in bridge in order to make this work, see " + "more details on the BCP bridging manual.", }, "bridge_horizon": { Type: schema.TypeInt, Optional: true, Description: "Used split-horizon value for the dynamically created bridge port. Can be used to " + "prevent bridging loops and isolate traffic. Set the same value for a group of ports, to " + "prevent them from sending data to ports with the same horizon value.", ValidateFunc: validation.IntAtLeast(1), }, "bridge_learning": { Type: schema.TypeString, Optional: true, Default: "default", Description: "Changes MAC learning behavior on the dynamically created bridge port: yes - enables MAC " + "learning no - disables MAC learning default - derive this value from the interface " + "default profile; same as yes if this is the interface default profile.", ValidateFunc: validation.StringInSlice([]string{"default", "no", "yes"}, false), }, "bridge_path_cost": { Type: schema.TypeInt, Optional: true, Description: "Used path cost for the dynamically created bridge port, used by STP/RSTP to determine " + "the best path, used by MSTP to determine the best path between regions. This property " + "has no effect when a bridge protocol-mode is set to none.", ValidateFunc: validation.IntAtLeast(0), }, "bridge_port_priority": { Type: schema.TypeInt, Optional: true, Description: "Used priority for the dynamically created bridge port, used by STP/RSTP to determine " + "the root port, used by MSTP to determine root port between regions. This property has " + "no effect when a bridge protocol-mode is set to none.", ValidateFunc: validation.IntBetween(0, 240), }, "change_tcp_mss": { Type: schema.TypeString, Optional: true, Default: "default", Description: "Modifies connection MSS settings (applies only for IPv4): yes - adjust connection MSS " + "value no - do not adjust connection MSS value default - derive this value from the " + "interface default profile; same as no if this is the interface default profile.", ValidateFunc: validation.StringInSlice([]string{"yes", "no", "default"}, false), }, KeyComment: PropCommentRw, KeyDefault: PropDefaultRo, "dhcpv6_lease_time": { Type: schema.TypeString, Optional: true, Description: "Lease time can be set starting from 7.20ab202, by default time is set to 1d.", DiffSuppressFunc: TimeEqual, }, "dhcpv6_pd_pool": { Type: schema.TypeString, Optional: true, Description: "Name of the IPv6 pool which will be used by dynamically created DHCPv6-PD server when " + "client connects. [Read more >>](https://wiki.mikrotik.com/wiki/Manual:IPv6_PD_over_PPP)", }, "dhcpv6_use_radius": { Type: schema.TypeBool, Optional: true, Description: "pecifies value for `use-radius` option selected for dynamically generated DHCPv6 PD servers.", }, "dns_server": { Type: schema.TypeSet, Optional: true, Description: "IP address of the DNS server that is supplied to ppp clients.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.IsIPv4Address, }, MaxItems: 2, }, "idle_timeout": { Type: schema.TypeString, Optional: true, Description: "Specifies the amount of time after which the link will be terminated if there are no " + "activity present. Timeout is not set by default.", DiffSuppressFunc: TimeEqual, }, "incoming_filter": { Type: schema.TypeString, Optional: true, Description: "Firewall chain name for incoming packets. Specified chain gets control for each packet " + "coming from the client. The ppp chain should be manually added and rules with " + "action=jump jump-target=ppp should be added to other relevant chains in order for this " + "feature to work. For more information look at the examples section.", }, "insert_queue_before": { Type: schema.TypeString, Optional: true, Description: "Specify where to place dynamic simple queue entries for static DCHP leases with rate-limit " + "parameter set.", }, "interface_list": { Type: schema.TypeString, Optional: true, Description: "Interface list name.", }, "local_address": { Type: schema.TypeString, Optional: true, Description: "Tunnel address or name of the pool from which address is assigned to ppp interface locally.", }, KeyName: PropName("PPP profile name."), "on_up": { Type: schema.TypeString, Optional: true, Description: "Execute script on user login-event. These are available variables that are accessible " + "for the event script:\n * user\n * local-address\n * remote-address\n * caller-id\n * called-id\n * interface.", }, "on_down": { Type: schema.TypeString, Optional: true, Description: "Execute script on user logging off. See on-up for more details.", }, "only_one": { Type: schema.TypeString, Optional: true, Default: "default", Description: "Defines whether a user is allowed to have more than one ppp session at a time yes - a " + "user is not allowed to have more than one ppp session at a time no - the user is allowed " + "to have more than one ppp session at a time default - derive this value from the " + "interface default profile; same as no if this is the interface default profile.", ValidateFunc: validation.StringInSlice([]string{"yes", "no", "default"}, false), }, "outgoing_filter": { Type: schema.TypeString, Optional: true, Description: "Firewall chain name for outgoing packets. The specified chain gets control for each " + "packet going to the client. The PPP chain should be manually added and rules with " + "action=jump jump-target=ppp should be added to other relevant chains in order for this " + "feature to work. For more information look at the Examples section.", }, "parent_queue": { Type: schema.TypeString, Optional: true, Description: "Name of parent simple queue.", }, "queue_type": { Type: schema.TypeString, Optional: true, Description: "Queue types.", }, "rate_limit": { Type: schema.TypeString, Optional: true, Description: "Rate limitation in form of rx-rate[/tx-rate] [rx-burst-rate[/tx-burst-rate] " + "[rx-burst-threshold[/tx-burst-threshold] [rx-burst-time[/tx-burst-time] [priority] " + "[rx-rate-min[/tx-rate-min]]]] from the point of view of the router (so 'rx' is client " + "upload, and 'tx' is client download). All rates are measured in bits per second, " + "unless followed by optional 'k' suffix (kilobits per second) or 'M' suffix (megabits " + "per second). If tx-rate is not specified, rx-rate serves as tx-rate too. The same " + "applies for tx-burst-rate, tx-burst-threshold and tx-burst-time. If both " + "rx-burst-threshold and tx-burst-threshold are not specified (but burst-rate is " + "specified), rx-rate and tx-rate are used as burst thresholds. If both rx-burst-time " + "and tx-burst-time are not specified, 1s is used as default. Priority takes values 1..8, " + "where 1 implies the highest priority, but 8 - the lowest. If rx-rate-min and " + "tx-rate-min are not specified rx-rate and tx-rate values are used. The rx-rate-min and " + "tx-rate-min values can not exceed rx-rate and tx-rate values.", }, "remote_address": { Type: schema.TypeString, Optional: true, Description: "Tunnel address or name of the pool from which address is assigned to remote ppp interface.", }, "remote_ipv6_prefix_pool": { Type: schema.TypeString, Optional: true, Description: "Assign prefix from IPv6 pool to the client and install corresponding IPv6 route.", }, "remote_ipv6_prefix_reuse": { Type: schema.TypeBool, Optional: true, Description: "If `remote-ipv6-prefix-pool` is specified and includes single `/64`prefix, then prefix can " + "be used only for a single PPP client for RADVD configuration. When this option is set to value `yes`, the " + "same prefix can be reused between all the clients using this PPP profile.", }, "session_timeout": { Type: schema.TypeString, Optional: true, Description: "Maximum time the connection can stay up. By default no time limit is set.", DiffSuppressFunc: TimeEqual, }, "use_compression": { Type: schema.TypeString, Optional: true, Default: "default", Description: "Specifies whether to use data compression or not. yes - enable data compression no - " + "disable data compression default - derive this value from the interface default profile; " + "same as no if this is the interface default profile This setting does not affect OVPN " + "tunnels.", ValidateFunc: validation.StringInSlice([]string{"yes", "no", "default"}, false), }, "use_encryption": { Type: schema.TypeString, Optional: true, Default: "default", Description: "Specifies whether to use data encryption or not. yes - enable data encryption no - " + "disable data encryption default - derive this value from the interface default profile; " + "same as no if this is the interface default profile require - explicitly requires " + "encryption This setting does not work on OVPN and SSTP tunnels.", ValidateFunc: validation.StringInSlice([]string{"yes", "no", "default", "require"}, false), }, "use_ipv6": { Type: schema.TypeString, Optional: true, Default: "default", Description: "Specifies whether to allow IPv6. By default is enabled if IPv6 package is installed. yes " + "- enable IPv6 support no - disable IPv6 support default - derive this value from the " + "interface default profile; same as no if this is the interface default profile require - " + "explicitly requires IPv6 support.", ValidateFunc: validation.StringInSlice([]string{"yes", "no", "default", "require"}, false), }, "use_mpls": { Type: schema.TypeString, Optional: true, Default: "default", Description: "Specifies whether to allow MPLS over PPP. yes - enable MPLS support no - disable MPLS " + "support default - derive this value from the interface default profile; same as no if " + "this is the interface default profile require - explicitly requires MPLS support", ValidateFunc: validation.StringInSlice([]string{"yes", "no", "default", "require"}, false), }, "use_upnp": { Type: schema.TypeString, Optional: true, Default: "default", Description: "Specifies whether to allow UPnP.", ValidateFunc: validation.StringInSlice([]string{"yes", "no", "default"}, false), }, "wins_server": { Type: schema.TypeSet, Optional: true, Description: "IP address of the WINS server to supply to Windows clients.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.IsIPv4Address, }, MaxItems: 2, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ppp_profile_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testPPPProfile = "routeros_ppp_profile.test" func TestAccPPPProfileTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ppp/profile", "routeros_ppp_profile"), Steps: []resource.TestStep{ { Config: testAccPPPProfileConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testPPPProfile), resource.TestCheckResourceAttr(testPPPProfile, "name", "profile1"), ), }, }, }) }) } } func testAccPPPProfileConfig() string { return providerConfig + ` resource "routeros_ppp_profile" "test" { name = "profile1" rate_limit = "10M/200k" use_upnp = "no" dns_server = ["8.8.8.8", "1.1.1.1"] } ` } ================================================ FILE: routeros/resource_ppp_secret.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "caller-id": "", "disabled": "false", >>>"ipv6-routes": "", "last-caller-id": "172.18.0.2", "last-disconnect-reason": "hung-up", "last-logged-out": "may/01/2023 20:52:13", "limit-bytes-in": "0", "limit-bytes-out": "0", "local-address": "172.18.0.2", "name": "user1", "password": "1", "profile": "ovpn", "routes": "", "service": "ovpn" } */ // https://help.mikrotik.com/docs/display/ROS/PPP+AAA#PPPAAA-UserDatabase func ResourcePPPSecret() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/ppp/secret"), MetaId: PropId(Id), "caller_id": { Type: schema.TypeString, Optional: true, Description: "For PPTP and L2TP it is the IP address a client must connect from. For PPPoE it is the " + "MAC address (written in CAPITAL letters) a client must connect from. For ISDN it is the " + "caller's number (that may or may not be provided by the operator) the client may " + "dial-in from.", }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "ipv6_routes": { Type: schema.TypeSet, Optional: true, Description: "IPv6 routes.", Elem: &schema.Schema{ Type: schema.TypeString, }, }, "last_caller_id": { Type: schema.TypeString, Computed: true, }, "last_disconnect_reason": { Type: schema.TypeString, Computed: true, }, "last_logged_out": { Type: schema.TypeString, Computed: true, }, "limit_bytes_in": { Type: schema.TypeInt, Optional: true, Default: 0, Description: "Maximal amount of bytes for a session that client can upload.", }, "limit_bytes_out": { Type: schema.TypeInt, Optional: true, Default: 0, Description: "Maximal amount of bytes for a session that client can download.", }, "local_address": { Type: schema.TypeString, Optional: true, Description: "IP address that will be set locally on ppp interface.", ValidateFunc: validation.IsIPv4Address, }, KeyName: PropName("Name used for authentication."), "password": { Type: schema.TypeString, Optional: true, Description: "Password used for authentication.", Sensitive: true, }, "profile": { Type: schema.TypeString, Optional: true, Default: "default", Description: "Which user profile to use.", }, "remote_address": { Type: schema.TypeString, Optional: true, Description: "IP address that will be assigned to remote ppp interface.", ValidateFunc: validation.IsIPv4Address, }, "remote_ipv6_prefix": { Type: schema.TypeString, Optional: true, Description: "IPv6 prefix assigned to ppp client. Prefix is added to ND prefix list enabling stateless " + "address auto-configuration on ppp interface.Available starting from v5.0.", }, "routes": { Type: schema.TypeSet, Optional: true, Description: "Routes that appear on the server when the client is connected. The route format is: " + "dst-address gateway metric (for example, 10.1.0.0/ 24 10.0.0.1 1). Other syntax is not " + "acceptable since it can be represented in incorrect way. Several routes may be " + "specified separated with commas. This parameter will be ignored for OpenVPN.", Elem: &schema.Schema{ Type: schema.TypeString, }, }, // The ROS 7.8 version does not contain the isdn option. "service": { Type: schema.TypeString, Optional: true, Default: "any", Description: "Specifies the services that particular user will be able to use.", ValidateFunc: validation.StringInSlice( []string{"any", "async", "isdn", "l2tp", "pppoe", "pptp", "ovpn", "sstp"}, false, ), }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_ppp_secret_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testPPPSecret = "routeros_ppp_secret.test" func TestAccPPPSecretTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/ppp/secret", "routeros_ppp_secret"), Steps: []resource.TestStep{ { Config: testAccPPPSecretConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testPPPSecret), resource.TestCheckResourceAttr(testPPPSecret, "name", "user-test"), ), }, }, }) }) } } func testAccPPPSecretConfig() string { return providerConfig + ` resource "routeros_ppp_secret" "test" { name = "user-test" password = "12345678" } ` } ================================================ FILE: routeros/resource_queue_simple.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*1", "bucket-size": "0.1/0.1", "burst-limit": "0/0", "burst-threshold": "0/0", "burst-time": "0s/0s", "bytes": "0/0", "comment": "comment", "disabled": "false", "dropped": "0/0", "dynamic": "false", "invalid": "false", "limit-at": "0/0", "max-limit": "0/0", "name": "queue1", "packet-marks": "", "packet-rate": "0/0", "packets": "0/0", "parent": "none", "priority": "8/8", "queue": "default-small/default-small", "queued-bytes": "0/0", "queued-packets": "0/0", "rate": "0/0", "target": "1.0.1.0/24", "total-bytes": "0", "total-dropped": "0", "total-packet-rate": "0", "total-packets": "0", "total-queued-bytes": "0", "total-queued-packets": "0", "total-rate": "0" } */ // https://help.mikrotik.com/docs/spaces/ROS/pages/328088/Queues#Queues-SimpleQueue // https://wiki.mikrotik.com/Manual:Queue#Simple_Queues func ResourceQueueSimple() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/queue/simple"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("bytes", "dropped", "packet_rate", "packets", "queued_bytes", "queued_packets", "rate", "total_bytes", "total_dropped", "total_packet_rate", "total_packets", "total_queued_bytes", "total_queued_packets", "total_rate"), "bucket_size": { Type: schema.TypeString, Optional: true, Description: "", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "burst_limit": { Type: schema.TypeString, Optional: true, Description: "Maximal upload/download data rate which can be reached while the burst is active.", DiffSuppressFunc: BitsEqual, }, "burst_threshold": { Type: schema.TypeString, Optional: true, Description: "When average data rate is below this value - burst is allowed, as soon as average data " + "rate reach this value - burst is denied (basically this is burst on/off switch). For optimal burst " + "behavior this value should above `limit-at` value and below `max-limit` value", DiffSuppressFunc: BitsEqual, }, "burst_time": { Type: schema.TypeString, Optional: true, Description: "Period of time, in seconds, over which the average upload/download data rate is calculated. " + "This is NOT the time of actual burst.", DiffSuppressFunc: TimeEqual, }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "dst": { Type: schema.TypeString, Optional: true, Description: "Allows to select only specific stream (from target address to this destination address) " + "for limitation explain what is target and what is dst and what is upload and what not.", }, KeyDynamic: PropDynamicRo, KeyInvalid: PropInvalidRo, "limit_at": { Type: schema.TypeString, Optional: true, Description: "Normal upload/download data rate that is guaranteed to a target.", DiffSuppressFunc: BitsEqual, }, "max_limit": { Type: schema.TypeString, Optional: true, Description: "Maximal upload/download data rate that is allowed for a target to reach to reach what.", DiffSuppressFunc: BitsEqual, }, KeyName: PropName("Queue name."), "packet_marks": { Type: schema.TypeSet, Optional: true, Description: "Allows to use marked packets from `/ip firewall mangle`. Take look at this packet flow diagram. " + "You need to make sure that packets are marked before the simple queues (before global-in HTB queue).", Elem: &schema.Schema{ Type: schema.TypeString, }, }, "parent": { Type: schema.TypeString, Optional: true, Description: "Assigns this queue as a child queue for selected target. Target queue can be HTB queue " + "or any other previously created queue.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "priority": { Type: schema.TypeString, Optional: true, Description: "Prioritize one child queue over other child queue. Does not work on parent queues (if queue " + "has at least one child). One is the highest, eight is the lowest priority. Child queue with higher " + "priority will have chance to reach its `max-limit` before child with lower priority. Priority have " + "nothing to do with bursts.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "queue": { Type: schema.TypeString, Optional: true, Description: "Choose the type of the queue.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "target": { Type: schema.TypeSet, Required: true, Description: "List of IP address ranges that will be limited by this queue.", Elem: &schema.Schema{ Type: schema.TypeString, }, }, "time": { Type: schema.TypeString, Optional: true, Description: "Allow to specify time when particular queue will be active. Router must have correct time settings.", DiffSuppressFunc: TimeEqual, }, "total_bucket_size": { Type: schema.TypeInt, Optional: true, Description: "", }, "total_burst_limit": { Type: schema.TypeInt, Optional: true, Description: "", }, "total_burst_threshold": { Type: schema.TypeInt, Optional: true, Description: "", }, "total_burst_time": { Type: schema.TypeInt, Optional: true, Description: "", DiffSuppressFunc: TimeEqual, }, "total_limit_at": { Type: schema.TypeInt, Optional: true, Description: "", }, "total_max_limit": { Type: schema.TypeInt, Optional: true, Description: "", }, "total_priority": { Type: schema.TypeInt, Optional: true, Description: "", }, "total_queue": { Type: schema.TypeString, Optional: true, Description: "", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_queue_simple_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testQueueSimple = "routeros_queue_simple.test" func TestAccQueueSimpleTest_basic(t *testing.T) { t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/queue/simple", "routeros_queue_simple"), Steps: []resource.TestStep{ { Config: testAccQueueSimpleConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testQueueSimple), resource.TestCheckResourceAttr(testQueueSimple, "name", "server"), resource.TestCheckResourceAttr(testQueueSimple, "target.0", "10.1.1.1/32"), resource.TestCheckResourceAttr(testQueueSimple, "max_limit", "20000000/20000000"), ), }, }, }) }) } } func testAccQueueSimpleConfig() string { return fmt.Sprintf(`%v resource "routeros_queue_simple" "test" { name = "server" target = ["10.1.1.1/32"] # issues/643 burst_limit = "30M/30M" burst_threshold = "25M/25M" burst_time = "10/10" max_limit = "20M/20M" } `, providerConfig) } ================================================ FILE: routeros/resource_queue_tree.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*1000000", "bucket-size": "0.1", "burst-limit": "0", "burst-threshold": "0", "burst-time": "0s", "bytes": "0", "comment": "comment", "disabled": "false", "dropped": "0", "invalid": "false", "limit-at": "0", "max-limit": "0", "name": "queue1", "packet-mark": "", "packet-rate": "0", "packets": "0", "parent": "global", "priority": "8", "queue": "default-small", "queued-bytes": "0", "queued-packets": "0", "rate": "0" } */ // https://help.mikrotik.com/docs/spaces/ROS/pages/328088/Queues#Queues-QueueTree // https://wiki.mikrotik.com/Manual:Queue#Queue_Tree func ResourceQueueTree() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/queue/tree"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("borrows", "bytes", "dropped", "lends", "packet_rate", "packets", "pcq_queues", "queued_bytes", "queued_packets", "rate"), "bucket_size": { Type: schema.TypeString, Optional: true, Description: "", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "burst_limit": { Type: schema.TypeString, Optional: true, Description: "Maximal data rate which can be reached while the burst is active.", DiffSuppressFunc: BitsEqual, }, "burst_threshold": { Type: schema.TypeString, Optional: true, Description: "When average data rate is below this value - burst is allowed, as soon as average data " + "rate reach this value - burst is denied (basically this is burst on/off switch). For optimal burst " + "behavior this value should above `limit-at` value and below `max-limit` value.", DiffSuppressFunc: BitsEqual, }, "burst_time": { Type: schema.TypeString, Optional: true, Description: "Period of time, in seconds, over which the average data rate is calculated. " + "This is NOT the time of actual burst.", DiffSuppressFunc: TimeEqual, }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, KeyInvalid: PropInvalidRo, "limit_at": { Type: schema.TypeString, Optional: true, Description: "Normal data rate that is guaranteed to a target.", DiffSuppressFunc: BitsEqual, }, "max_limit": { Type: schema.TypeString, Optional: true, Description: "Maximal data rate that is allowed for a target to reach.", DiffSuppressFunc: BitsEqual, }, KeyName: PropName("Queue tree name."), "packet_mark": { Type: schema.TypeSet, Optional: true, Description: "Allows to use marked packets from `/ip firewall mangle`. Take look at this packet flow diagram. " + "You need to make sure that packets are marked before the simple queues (before global-in HTB queue).", Elem: &schema.Schema{ Type: schema.TypeString, }, }, "parent": { Type: schema.TypeString, Required: true, Description: "Assigns this queue as a child queue for selected target. Target queue can be HTB queue " + "or any other previously created queue.", }, "priority": { Type: schema.TypeInt, Optional: true, Description: "Prioritize one child queue over other child queue. Does not work on parent queues (if queue " + "has at least one child). One is the highest, eight is the lowest priority. Child queue with higher " + "priority will have chance to reach its `max-limit` before child with lower priority. Priority have " + "nothing to do with bursts.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "queue": { Type: schema.TypeString, Optional: true, Description: "Choose the type of the queue.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_queue_tree_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testQueueTree = "routeros_queue_tree.test" func TestAccQueueTreeTest_basic(t *testing.T) { t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/queue/tree", "routeros_queue_tree"), Steps: []resource.TestStep{ { Config: testAccQueueTreeConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testQueueTree), resource.TestCheckResourceAttr(testQueueTree, "name", "qt-test"), resource.TestCheckResourceAttr(testQueueTree, "parent", "global"), resource.TestCheckResourceAttr(testQueueTree, "packet_mark.0", "pmark-test"), resource.TestCheckResourceAttr(testQueueTree, "max_limit", "10000000"), ), }, }, }) }) } } func testAccQueueTreeConfig() string { return fmt.Sprintf(`%v resource "routeros_queue_tree" "test" { name = "qt-test" parent = "global" max_limit = "10M" packet_mark = ["pmark-test"] } `, providerConfig) } ================================================ FILE: routeros/resource_queue_type.go ================================================ package routeros import ( "time" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*FFFFFFFA", "default": "true", "kind": "pcq", "name": "pcq-upload-default", "pcq-burst-rate": "0", "pcq-burst-threshold": "0", "pcq-burst-time": "10s", "pcq-classifier": "src-address", "pcq-dst-address-mask": "32", "pcq-dst-address6-mask": "128", "pcq-limit": "50", "pcq-rate": "0", "pcq-src-address-mask": "32", "pcq-src-address6-mask": "128", "pcq-total-limit": "2000" }, */ // https://help.mikrotik.com/docs/spaces/ROS/pages/328088/Queues#Queues-QueueTypes // https://wiki.mikrotik.com/Manual:Queue#Queue_Types func ResourceQueueType() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/queue/type"), MetaId: PropId(Id), KeyDefault: PropDefaultRo, "kind": { Type: schema.TypeString, Required: true, Description: "Queue kind.", }, KeyName: PropName("Type name."), // BFIFO "bfifo_limit": { Type: schema.TypeInt, Optional: true, Description: "Maximum number of bytes that the BFIFO queue can hold. Applies if `kind` is `bfifo`.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, // MQ PFIFO "mq_pfifo_limit": { Type: schema.TypeInt, Optional: true, Description: "Multi-queue PFIFO limit.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, // PFIFO "pfifo_limit": { Type: schema.TypeInt, Optional: true, Description: "Maximum number of packets that the PFIFO queue can hold. Applies if `kind` is `pfifo`.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, // RED "red_avg_packet": { Type: schema.TypeInt, Optional: true, Description: "Used by RED for average queue size calculations (for packet to byte translation).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "red_burst": { Type: schema.TypeInt, Optional: true, Description: "Number of packets allowed for bursts of packets when there are no packets in the queue.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "red_limit": { Type: schema.TypeInt, Optional: true, Description: "RED queue limit in packets.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "red_max_threshold": { Type: schema.TypeInt, Optional: true, Description: "The average queue size at which packet marking probability is the highest.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "red_min_threshold": { Type: schema.TypeInt, Optional: true, Description: "Average queue size in bytes.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, // SFQ "sfq_allot": { Type: schema.TypeInt, Optional: true, Description: "Amount of data in bytes that can be sent in one round-robin round.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "sfq_perturb": { Type: schema.TypeInt, Optional: true, Description: "How often hash function must be refreshed.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, // PCQ "pcq_burst_rate": { Type: schema.TypeInt, Optional: true, Description: "Maximal upload/download data rate which can be reached while the burst for substream is allowed.", DiffSuppressFunc: BitsEqual, }, "pcq_burst_threshold": { Type: schema.TypeInt, Optional: true, Description: "This is value of burst on/off switch.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "pcq_burst_time": { Type: schema.TypeString, Optional: true, Description: "Period of time, in seconds, over which the average data rate is calculated. (This is " + "NOT the time of actual burst).", DiffSuppressFunc: TimeEqual, }, "pcq_classifier": { Type: schema.TypeSet, Optional: true, Description: "Selection of sub-stream identifiers.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"src-address", "dst-address", "src-port", "dst-port"}, false), }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "pcq_dst_address_mask": { Type: schema.TypeInt, Optional: true, Description: "Size of IPv4 network that will be used as dst-address sub-stream identifier.", ValidateFunc: validation.IntBetween(0, 32), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "pcq_dst_address6_mask": { Type: schema.TypeInt, Optional: true, Description: "Size of IPV6 network that will be used as dst-address sub-stream identifier.", ValidateFunc: validation.IntBetween(0, 128), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "pcq_limit": { Type: schema.TypeInt, Optional: true, Description: "Queue size of a single sub-stream (in kilobytes).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "pcq_rate": { Type: schema.TypeInt, Optional: true, Description: "Maximal available data rate of each sub-steam.", DiffSuppressFunc: BitsEqual, }, "pcq_src_address_mask": { Type: schema.TypeInt, Optional: true, Description: "Size of IPv4 network that will be used as src-address sub-stream identifier.", ValidateFunc: validation.IntBetween(0, 32), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "pcq_src_address6_mask": { Type: schema.TypeInt, Optional: true, Description: "Size of IPV6 network that will be used as src-address sub-stream identifier.", ValidateFunc: validation.IntBetween(0, 128), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "pcq_total_limit": { Type: schema.TypeInt, Optional: true, Description: "Max amount of bytes queued (in kilobytes) for all sub-streams per PCQ instance. " + "Note that each queue tree entry has its own PCQ instance.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, // CoDel "codel_ce_threshold": { Type: schema.TypeInt, Optional: true, Description: "Marks packets above a configured threshold with ECN.", DiffSuppressFunc: TimeEqualU(time.Nanosecond), }, "codel_ecn": { Type: schema.TypeBool, Optional: true, Description: "An option is used to mark packets instead of dropping them.", }, "codel_interval": { Type: schema.TypeString, Optional: true, Description: "Interval should be set on the order of the worst-case RTT through the bottleneck giving " + "endpoints sufficient time to react.", DiffSuppressFunc: TimeEqualU(time.Millisecond), }, "codel_limit": { Type: schema.TypeInt, Optional: true, Description: "Queue limit, when the limit is reached, incoming packets are dropped.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "codel_target": { Type: schema.TypeString, Optional: true, Description: "Represents an acceptable minimum persistent queue delay.", DiffSuppressFunc: TimeEqualU(time.Millisecond), }, // FQ-Codel "fq_codel_ce_threshold": { Type: schema.TypeInt, Optional: true, Description: "Marks packets above a configured threshold with ECN.", DiffSuppressFunc: TimeEqualU(time.Nanosecond), }, "fq_codel_ecn": { Type: schema.TypeBool, Optional: true, Description: "An option is used to mark packets instead of dropping them.", }, "fq_codel_flows": { Type: schema.TypeInt, Optional: true, Description: "A number of flows into which the incoming packets are classified.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "fq_codel_interval": { Type: schema.TypeString, Optional: true, Description: "Interval should be set on the order of the worst-case RTT through the bottleneck giving " + "endpoints sufficient time to react.", DiffSuppressFunc: TimeEqualU(time.Millisecond), }, "fq_codel_limit": { Type: schema.TypeInt, Optional: true, Description: "Queue limit, when the limit is reached, incoming packets are dropped.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "fq_codel_memlimit": { Type: schema.TypeInt, Optional: true, Description: "A total number of bytes that can be queued in this FQ-CoDel instance. Will be enforced " + "from the fq-codel-limit parameter.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "fq_codel_quantum": { Type: schema.TypeInt, Optional: true, Description: "A number of bytes used as 'deficit' in the fair queuing algorithm. Default (1514 bytes) " + "corresponds to the Ethernet MTU plus the hardware header length of 14 bytes.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "fq_codel_target": { Type: schema.TypeString, Optional: true, Description: "Represents an acceptable minimum persistent queue delay.", DiffSuppressFunc: TimeEqualU(time.Millisecond), }, // CAKE "cake_ack_filter": { Type: schema.TypeString, Optional: true, Description: "", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "cake_atm": { Type: schema.TypeString, Optional: true, Description: "Compensates for ATM cell framing, which is normally found on ADSL links.", }, "cake_autorate_ingress": { Type: schema.TypeBool, Optional: true, Description: "Automatic capacity estimation based on traffic arriving at this qdisc. This is most likely " + "to be useful with cellular links, which tend to change quality randomly. The Bandwidth Limit " + "parameter can be used in conjunction to specify an initial estimate. The shaper will periodically be " + "set to a bandwidth slightly below the estimated rate. This estimator cannot estimate the bandwidth " + "of links downstream of itself.", }, "cake_bandwidth": { Type: schema.TypeInt, Optional: true, Description: "Sets the shaper bandwidth.", DiffSuppressFunc: BitsEqual, }, "cake_diffserv": { Type: schema.TypeString, Optional: true, Description: "CAKE can divide traffic into `tins` based on the Diffserv field:" + "\n * `diffserv4` Provides a general-purpose Diffserv implementation with four tins: Bulk (CS1), " + "6.25% threshold, generally low priority. Best Effort (general), 100% threshold. Video (AF4x, AF3x, " + "CS3, AF2x, CS2, TOS4, TOS1), 50% threshold. Voice (CS7, CS6, EF, VA, CS5, CS4), 25% threshold." + "\n * `diffserv3` (default) Provides a simple, general-purpose Diffserv implementation with three " + "tins: Bulk (CS1), 6.25% threshold, generally low priority. Best Effort (general), 100% threshold. " + "Voice (CS7, CS6, EF, VA, TOS4), 25% threshold, reduced Codel interval.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "cake_flowmode": { Type: schema.TypeString, Optional: true, Description: "\n * `flowblind` - Disables flow isolation; all traffic passes through a single queue for each tin." + "\n * `srchost` - Flows are defined only by source address." + "\n * `dsthost` Flows are defined only by destination address." + "\n * `hosts` - Flows are defined by source-destination host pairs. This is host isolation, rather " + "than flow isolation." + "\n * `flows` - Flows are defined by the entire 5-tuple of source address, a destination address, " + "transport protocol, source port, and destination port. This is the type of flow isolation performed " + "by SFQ and fq_codel." + "\n * `dual-srchost` Flows are defined by the 5-tuple, and fairness is applied first over source " + "addresses, then over individual flows. Good for use on egress traffic from a LAN to the internet, " + "where it'll prevent any LAN host from monopolizing the uplink, regardless of the number of flows they use." + "\n * `dual-dsthost` Flows are defined by the 5-tuple, and fairness is applied first over destination " + "addresses, then over individual flows. Good for use on ingress traffic to a LAN from the internet, " + "where it'll prevent any LAN host from monopolizing the downlink, regardless of the number of flows they use." + "\n * `triple-isolate` - Flows are defined by the 5-tuple, and fairness is applied over source *and* " + "destination addresses intelligently (ie. not merely by host-pairs), and also over individual flows." + "\n * `nat` Instructs Cake to perform a NAT lookup before applying flow- isolation rules, to determine " + "the true addresses and port numbers of the packet, to improve fairness between hosts `inside` the NAT. " + "This has no practical effect in `flowblind` or `flows` modes, or if NAT is performed on a different host." + "\n * `nonat` (default) The cake will not perform a NAT lookup. Flow isolation will be performed using " + "the addresses and port numbers directly visible to the interface Cake is attached to.", ValidateFunc: validation.StringInSlice([]string{"dsthost", "dual-dsthost", "dual-srchost", "flowblind", "flows", "hosts", "srchost", "triple-isolate"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "cake_memlimit": { Type: schema.TypeInt, Optional: true, Description: "Limit the memory consumed by Cake to LIMIT bytes. By default, the limit is calculated based " + "on the bandwidth and RTT settings.", }, "cake_mpu": { Type: schema.TypeInt, Optional: true, Description: "Rounds each packet (including overhead) up to a minimum length BYTES. ", ValidateFunc: validation.IntBetween(-64, 256), }, "cake_nat": { Type: schema.TypeBool, Optional: true, Description: "Instructs Cake to perform a NAT lookup before applying a flow-isolation rule.", }, "cake_overhead": { Type: schema.TypeInt, Optional: true, Description: "Adds BYTES to the size of each packet. BYTES may be negative.", ValidateFunc: validation.IntBetween(-64, 256), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "cake_overhead_scheme": { Type: schema.TypeString, Optional: true, Description: "", }, "cake_rtt": { Type: schema.TypeString, Optional: true, Description: "Manually specify an RTT. Default 100ms is suitable for most Internet traffic.", DiffSuppressFunc: TimeEqualU(time.Millisecond), }, "cake_rtt_scheme": { Type: schema.TypeString, Optional: true, Description: "\n * `datacentre` - For extremely high-performance 10GigE+ networks only. Equivalent to `RTT 100us`." + "\n * `lan` - For pure Ethernet (not Wi-Fi) networks, at home or in the office. Don't use this when " + "shaping for an Internet access link. Equivalent to `RTT 1ms`." + "\n * `metro` - For traffic mostly within a single city. Equivalent to `RTT 10ms`. regional For traffic " + "mostly within a European-sized country. Equivalent to `RTT 30ms`." + "\n * `internet` (default) This is suitable for most Internet traffic. Equivalent to `RTT 100ms`." + "\n * `oceanic` - For Internet traffic with generally above-average latency, such as that suffered by " + "Australasian residents. Equivalent to `RTT 300ms`." + "\n * `satellite` - For traffic via geostationary satellites. Equivalent to `RTT 1000ms`." + "\n * `interplanetary` - So named because Jupiter is about 1 light-hour from Earth. Use this to (almost) " + "completely disable AQM actions. Equivalent to `RTT 3600s`.", ValidateFunc: validation.StringInSlice([]string{"datacentre", "internet", "interplanetary", "lan", "metro", "none", "oceanic", "regional", "satellite"}, false), }, "cake_wash": { Type: schema.TypeBool, Optional: true, Description: "Apply the wash option to clear all extra DiffServ (but not ECN bits), after priority queuing " + "has taken place.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_queue_type_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testQueueType = "routeros_queue_type.test" func TestAccQueueTypeTest_basic(t *testing.T) { t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/queue/type", "routeros_queue_type"), Steps: []resource.TestStep{ { Config: testAccQueueTypeConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testQueueType), resource.TestCheckResourceAttr(testQueueType, "name", "pcq-test"), resource.TestCheckResourceAttr(testQueueType, "kind", "pcq"), resource.TestCheckResourceAttr(testQueueType, "pcq_rate", "0"), resource.TestCheckResourceAttr(testQueueType, "pcq_limit", "50"), resource.TestCheckResourceAttr(testQueueType, "pcq_classifier.0", "dst-address"), ), }, }, }) }) } } func testAccQueueTypeConfig() string { return fmt.Sprintf(`%v resource "routeros_queue_type" "test" { name = "pcq-test" kind = "pcq" pcq_rate = 0 pcq_limit = 50 pcq_classifier = ["dst-address"] } `, providerConfig) } ================================================ FILE: routeros/resource_radius.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) // https://help.mikrotik.com/docs/display/ROS/RADIUS#RADIUS-RADIUSClient func ResourceRadius() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/radius"), MetaId: PropId(Id), "accounting_backup": { Type: schema.TypeBool, Optional: true, Default: false, Description: "An option whether the configuration is for the backup RADIUS server.", }, "accounting_port": { Type: schema.TypeInt, Optional: true, Default: 1813, Description: "RADIUS server port used for accounting.", ValidateFunc: Validation64k, }, "address": { Type: schema.TypeString, Required: true, Description: "IPv4 or IPv6 address of RADIUS server.", }, "authentication_port": { Type: schema.TypeInt, Optional: true, Default: 1812, Description: "RADIUS server port used for authentication.", ValidateFunc: Validation64k, }, "called_id": { Type: schema.TypeString, Optional: true, Description: "RADIUS calling station identifier.", }, "certificate": { Type: schema.TypeString, Optional: true, Default: "none", Description: "Certificate to use for communication with RADIUS Server with RadSec enabled.", }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "domain": { Type: schema.TypeString, Optional: true, Description: "Microsoft Windows domain of client passed to RADIUS servers that require domain validation.", }, "protocol": { Type: schema.TypeString, Optional: true, Default: "udp", Description: "An option specifies the protocol to use when communicating with the RADIUS Server.", ValidateFunc: validation.StringInSlice([]string{"radsec", "udp"}, false), }, "radsec_timeout": { Type: schema.TypeString, Optional: true, Description: "Timeout after which the request should be resent over RadSec protocol.", DiffSuppressFunc: TimeEqual, }, "realm": { Type: schema.TypeString, Optional: true, Description: "Explicitly stated realm (user domain), so the users do not have to provide proper ISP domain name in the user name.", }, "require_message_auth": { Type: schema.TypeString, Optional: true, Description: "An option whether to require `Message-Authenticator` in received Access-Accept/Challenge/Reject messages.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.StringInSlice([]string{"no", "yes-for-request-resp"}, false), }, "secret": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "The shared secret to access the RADIUS server.", }, "service": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"hotspot", "login", "ppp", "wireless", "dhcp", "ipsec", "dot1x"}, false), }, Description: "A set of router services that will use the RADIUS server. Possible values: " + "`hotspot`, `login`, `ppp`, `wireless`, `dhcp`, `ipsec`, `dot1x`.", }, "src_address": { Type: schema.TypeString, Optional: true, Description: "Source IPv4/IPv6 address of the packets sent to the RADIUS server.", ValidateFunc: validation.IsIPAddress, }, "status": { Type: schema.TypeString, Computed: true, }, "timeout": { Type: schema.TypeString, Optional: true, Default: "300ms", Description: "A timeout, after which the request should be resent.", DiffSuppressFunc: TimeEqual, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, SchemaVersion: 1, StateUpgraders: []schema.StateUpgrader{ { Type: ResourceRadiusV0().CoreConfigSchema().ImpliedType(), Upgrade: stateMigrationScalarToList("service"), Version: 0, }, }, } } // https://help.mikrotik.com/docs/display/ROS/RADIUS#RADIUS-ConnectionTerminatingfromRADIUS func ResourceRadiusIncoming() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/radius/incoming"), MetaId: PropId(Name), "accept": { Type: schema.TypeBool, Optional: true, Default: false, Description: "An option whether to accept the unsolicited messages.", }, "port": { Type: schema.TypeInt, Optional: true, Default: 3799, Description: "The port number to listen for the requests on.", ValidateFunc: Validation64k, }, "vrf": { Type: schema.TypeString, Optional: true, Description: "VRF on which service is listening for incoming connections. This option is available in RouterOS starting from version 7.4.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_radius_v0.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) // https://help.mikrotik.com/docs/display/ROS/RADIUS#RADIUS-RADIUSClient func ResourceRadiusV0() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/radius"), MetaId: PropId(Id), "accounting_backup": { Type: schema.TypeBool, Optional: true, Default: false, Description: "An option whether the configuration is for the backup RADIUS server.", }, "accounting_port": { Type: schema.TypeInt, Optional: true, Default: 1813, Description: "RADIUS server port used for accounting.", ValidateFunc: Validation64k, }, "address": { Type: schema.TypeString, Required: true, Description: "IPv4 or IPv6 address of RADIUS server.", ValidateFunc: validation.IsIPAddress, }, "authentication_port": { Type: schema.TypeInt, Optional: true, Default: 1812, Description: "RADIUS server port used for authentication.", ValidateFunc: Validation64k, }, "called_id": { Type: schema.TypeString, Optional: true, Description: "RADIUS calling station identifier.", }, "certificate": { Type: schema.TypeString, Optional: true, Default: "none", Description: "Certificate to use for communication with RADIUS Server with RadSec enabled.", }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "domain": { Type: schema.TypeString, Optional: true, Description: "Microsoft Windows domain of client passed to RADIUS servers that require domain validation.", }, "protocol": { Type: schema.TypeString, Optional: true, Default: "udp", Description: "An option specifies the protocol to use when communicating with the RADIUS Server.", ValidateFunc: validation.StringInSlice([]string{"radsec", "udp"}, false), }, "realm": { Type: schema.TypeString, Optional: true, Description: "Explicitly stated realm (user domain), so the users do not have to provide proper ISP domain name in the user name.", }, "secret": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "The shared secret to access the RADIUS server.", }, "service": { Type: schema.TypeString, Optional: true, Description: "A comma-separated list of router services that will use the RADIUS server. Possible values: " + "`hotspot`, `login`, `ppp`, `wireless`, `dhcp`, `ipsec`, `dot1x`.", }, "src_address": { Type: schema.TypeString, Optional: true, Description: "Source IPv4/IPv6 address of the packets sent to the RADIUS server.", ValidateFunc: validation.IsIPAddress, }, "timeout": { Type: schema.TypeString, Optional: true, Default: "300ms", Description: "A timeout, after which the request should be resent.", DiffSuppressFunc: TimeEqual, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_routing_bfd_configuration.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", ".nextid": "*2", "address-list": "", "addresses": "0.0.0.0/0,0.0.0.0/0", "disabled": "false", "forbid-bfd": "false", "inactive": "false", "interfaces": "lo,lo", "min-rx": "200ms", "min-tx": "200ms", "multiplier": "5", "vrf": "main" }, */ // https://help.mikrotik.com/docs/spaces/ROS/pages/331612210/routing+bfd // https://help.mikrotik.com/docs/spaces/ROS/pages/191299691/BFD func ResourceRoutingBfdConfiguration() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/routing/bfd/configuration"), MetaId: PropId(Id), "address_list": { Type: schema.TypeString, Optional: true, Description: "Name of the address list in which users IP address will be added.", }, "addresses": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.IsIPAddress, }, Description: "Set of IP (v4 or v6) addresses or CIDR networks.", }, KeyDisabled: PropDisabledRw, "forbid_bfd": { Type: schema.TypeBool, Optional: true, Description: "", }, KeyInactive: PropInactiveRo, "interfaces": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "List of interfaces.", }, "min_rx": { Type: schema.TypeString, Optional: true, Description: "", DiffSuppressFunc: TimeEqual, }, "min_tx": { Type: schema.TypeString, Optional: true, Description: "", DiffSuppressFunc: TimeEqual, }, "multiplier": { Type: schema.TypeInt, Optional: true, Description: "", }, KeyVrf: PropVrfRw, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_routing_bfd_configuration_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testRoutingBfdMinVersion = "7.20" const testRoutingBfdConfiguration = "routeros_routing_bfd_configuration.test" func TestAccRoutingBfdConfigurationTest_basic(t *testing.T) { if !testCheckMinVersion(t, testRoutingBfdMinVersion) { t.Logf("Test skipped, the minimum required version is %v", testRoutingBfdMinVersion) return } t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/routing/bfd/configuration", "routeros_routing_bfd_configuration"), Steps: []resource.TestStep{ { Config: testAccRoutingBfdConfigurationConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testRoutingBfdConfiguration), resource.TestCheckResourceAttr(testRoutingBfdConfiguration, "vrf", "main"), resource.TestCheckResourceAttr(testRoutingBfdConfiguration, "forbid_bfd", "true"), ), }, { Config: testAccRoutingBfdConfigurationConfig2(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testRoutingBfdConfiguration), resource.TestCheckResourceAttr(testRoutingBfdConfiguration, "vrf", "main"), resource.TestCheckResourceAttr(testRoutingBfdConfiguration, "interfaces.#", "0"), ), }, }, }) }) } } func testAccRoutingBfdConfigurationConfig() string { return fmt.Sprintf(`%v resource "routeros_routing_bfd_configuration" "test" { interfaces = ["lo", "ether2"] vrf = "main" forbid_bfd = true } `, providerConfig) } func testAccRoutingBfdConfigurationConfig2() string { return fmt.Sprintf(`%v resource "routeros_routing_bfd_configuration" "test" { vrf = "main" } `, providerConfig) } ================================================ FILE: routeros/resource_routing_bgp_connection.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "add-path-out": "none", "address-families": "ip", "as": "65521/1", "as-override": "true", "cisco-vpls-nlri-len-fmt": "auto-bits", "cluster-id": "0.0.0.0", "connect": "true", "disabled": "false", "hold-time": "infinity", "inactive": "false", "input.accept-communities": "", "input.accept-ext-communities": "", "input.accept-large-communities": "", "input.accept-nlri": "", "input.accept-unknown": "", "input.affinity": "alone", "input.allow-as": "0", "input.filter": "", "input.ignore-as-path-len": "true", "input.limit-process-routes-ipv4": "5", "input.limit-process-routes-ipv6": "5", "keepalive-time": "3m", "listen": "true", "local.address": "127.0.0.1", "local.port": "22334", "local.role": "ibgp", "local.ttl": "3", "multihop": "true", "name": "bgp1", "nexthop-choice": "default", "output.affinity": "alone", "output.default-originate": "never", "output.default-prepend": "1", "output.filter-chain": "", "output.filter-select": "", "output.keep-sent-attributes": "true", "output.network": "", "output.no-client-to-client-reflection": "false", "output.no-early-cut": "true", "output.redistribute": "rip,bgp", "remote.address": "0.0.0.0/32", "remote.allowed-as": "1111", "remote.port": "11223", "remote.ttl": "3", "remove-private-as": "true", "router-id": "0.0.0.1", "routing-table": "main", "save-to": "bgp.dump", "tcp-md5-key": "poipoipoipoipoi", "templates": "test-template", "use-bfd": "true", "vrf": "main" } */ // https://help.mikrotik.com/docs/display/ROS/BGP#BGP-ConnectionMenu // https://help.mikrotik.com/docs/spaces/ROS/pages/331612228/routing+bgp func ResourceRoutingBgpConnection() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/routing/bgp/connection"), MetaId: PropId(Id), MetaSetUnsetFields: PropSetUnsetFields("hold_time", "keepalive_time"), "add_path_out": { Type: schema.TypeString, Optional: true, Description: "", Default: "none", ValidateFunc: validation.StringInSlice([]string{"all", "none"}, false), }, "address_families": { Type: schema.TypeString, Optional: true, Default: "ip", Description: "List of address families about which this peer will exchange routing information. The " + "remote peer must support (they usually do) BGP capabilities optional parameter to " + "negotiate any other families than IP.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"ip", "ipv6", "l2vpn", "l2vpn-cisco", "vpnv4"}, false, false), }, "as": { Type: schema.TypeString, Required: true, Description: "32-bit BGP autonomous system number. Value can be entered in AS-Plain and AS-Dot " + "formats. The parameter is also used to set up the BGP confederation, in the following " + "format: confederation_as/as . For example, if your AS is 34 and your confederation AS is " + "43, then as configuration should be as =43/34.", }, "cisco_vpls_nlri_len_fmt": { Type: schema.TypeString, Optional: true, Description: "VPLS NLRI length format type. Used for compatibility with Cisco VPLS.", ValidateFunc: validation.StringInSlice([]string{"auto-bits", "auto-bytes", "bits", "bytes"}, false), }, "cluster_id": { Type: schema.TypeString, Optional: true, Description: "In case this instance is a route reflector: the cluster ID of the router reflector " + "cluster to this instance belongs. This attribute helps to recognize routing updates " + "that come from another route reflector in this cluster and avoid routing information " + "looping. Note that normally there is only one route reflector in a cluster; in this " + "case, 'cluster-id' does not need to be configured and BGP router ID is used instead.", ValidateFunc: validation.IsIPv4Address, Deprecated: DeprecatedInfo("7.20"), }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "connect": { Type: schema.TypeBool, Optional: true, Default: true, Description: "Whether to allow the router to initiate the connection.", }, "hold_time": { Type: schema.TypeString, Optional: true, Computed: true, Description: "Specifies the BGP Hold Time value to use when negotiating with peers. According to the " + "BGP specification, if the router does not receive successive KEEPALIVE and/or UPDATE " + "and/or NOTIFICATION messages within the period specified in the Hold Time field of the " + "OPEN message, then the BGP connection to the peer will be closed. The minimal hold-time " + "value of both peers will be actually used (note that the special value 0 or 'infinity' " + "is lower than any other value) infinity - never expire the connection and never send " + "keepalive messages.", }, "inactive": { Type: schema.TypeBool, Computed: true, }, "input": { Type: schema.TypeList, Optional: true, Description: "A group of parameters associated with BGP input.", MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "accept_communities": { Type: schema.TypeString, Optional: true, Description: "A quick way to filter incoming updates with specific communities. It allows filtering " + "incoming messages directly before they are even parsed and stored in memory, that way " + "significantly reducing memory usage. Regular input filter chain can only reject " + "prefixes which means that it will still eat memory and will be visible in /routing " + "route table as 'not active, filtered'. Changes to be applied required session refresh.", }, "accept_ext_communities": { Type: schema.TypeString, Optional: true, Description: "A quick way to filter incoming updates with specific extended communities. It allows " + "filtering incoming messages directly before they are even parsed and stored in memory, " + "that way significantly reducing memory usage. Regular input filter chain can only " + "reject prefixes which means that it will still eat memory and will be visible in " + "/routing route table as 'not active, filtered'. Changes to be applied required session " + "refresh.", }, "accept_large_communities": { Type: schema.TypeString, Optional: true, Description: "A quick way to filter incoming updates with specific large communities. It allows " + "filtering incoming messages directly before they are even parsed and stored in memory, " + "that way significantly reducing memory usage. Regular input filter chain can only " + "reject prefixes which means that it will still eat memory and will be visible in " + "/routing route table as 'not active, filtered'. Changes to be applied required session " + "refresh.", }, "accept_nlri": { Type: schema.TypeString, Optional: true, Description: "Name of the ipv4/6 address-list. A quick way to filter incoming updates with specific " + "NLRIs. It allows filtering incoming messages directly before they are even parsed and " + "stored in memory, that way significantly reducing memory usage. Regular input filter " + "chain can only reject prefixes which means that it will still eat memory and will be " + "visible in /routing route table as 'not active, filtered'. Changes to be applied " + "required session restart.", }, "accept_unknown": { Type: schema.TypeString, Optional: true, Description: "A quick way to filter incoming updates with specific 'unknown' attributes. It allows " + "filtering incoming messages directly before they are even parsed and stored in memory, " + "that way significantly reducing memory usage. Regular input filter chain can only " + "reject prefixes which means that it will still eat memory and will be visible in " + "/routing route table as 'not active, filtered'. Changes to be applied required session " + "refresh.", Deprecated: DeprecatedInfo("7.19"), }, // affinity (afi | alone | instance | main | remote-as | vrf; Default: ) // May be "0" "affinity": { Type: schema.TypeString, Optional: true, Description: "Configure input multi-core processing. Read more in Routing Protocol Multi-core Support " + "article. alone - input and output of each session are processed in its own process, " + "most likely the best option when there are a lot of cores and a lot of peers afi, " + "instance, vrf, remote-as - try to run input/output of new session in process with " + "similar parameters main - run input/output in the main process (could potentially " + "increase performance on single-core even possibly on multi-core devices with a small " + "amount of cores) input - run output in the same process as input (can be set only for " + "output affinity)", }, "allow_as": { Type: schema.TypeInt, Optional: true, Description: "Indicates how many times to allow your own AS number in AS-PATH, before discarding a " + "prefix.", ValidateFunc: validation.IntBetween(0, 10), }, "filter": { Type: schema.TypeString, Optional: true, Description: "Name of the routing filter chain to be used on input prefixes. This happens after " + "NLRIs are processed. If the chain is not specified, then BGP by default accepts " + "everything.", }, "filter_communities": { Type: schema.TypeString, Optional: true, Description: "A quick way to filter incoming updates with specific communities. It allows filtering " + "incoming messages directly before they are even parsed and stored in memory, that way significantly " + "reducing memory usage. Regular input filter chain can only reject prefixes which means that it will " + "still eat memory and will be visible in /routing route table as \"not active, filtered\". Changes " + "to be applied required session refresh.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "filter_ext_communities": { Type: schema.TypeString, Optional: true, Description: "A quick way to filter incoming updates with specific extended communities. It allows filtering " + "incoming messages directly before they are even parsed and stored in memory, that way significantly " + "reducing memory usage. Regular input filter chain can only reject prefixes which means that it will " + "still eat memory and will be visible in /routing route table as \"not active, filtered\". Changes " + "to be applied required session refresh.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "filter_large_communities": { Type: schema.TypeString, Optional: true, Description: "A quick way to filter incoming updates with specific large communities. It allows filtering " + "incoming messages directly before they are even parsed and stored in memory, that way significantly " + "reducing memory usage. Regular input filter chain can only reject prefixes which means that it will " + "still eat memory and will be visible in /routing route table as \"not active, filtered\". Changes " + "to be applied required session refresh.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "filter_nlri": { Type: schema.TypeString, Optional: true, Description: "Name of the filter chain that will filter incoming IPv4/IPv6 NLRIs directly before " + "they are stored in memory, that way significantly reducing memory usage. Regular input filter " + "chain can only reject prefixes which means that it will still eat memory and will be visible in " + "`/routing route` table as `not active, filtered`. Changes to be applied required session restart.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "filter_unknown": { Type: schema.TypeString, Optional: true, Description: "A quick way to filter incoming updates with specific \"unknown\" attributes. It allows filtering " + "incoming messages directly before they are even parsed and stored in memory, that way significantly " + "reducing memory usage. Regular input filter chain can only reject prefixes which means that it will " + "still eat memory and will be visible in /routing route table as \"not active, filtered\". Changes " + "to be applied required session refresh.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "ignore_as_path_len": { Type: schema.TypeBool, Optional: true, Description: "Whether to ignore the AS_PATH attribute in the BGP route selection algorithm", Deprecated: DeprecatedInfo("7.20"), }, // FIXME ROS 7.8: 'unknown parameter input.limit-nlri-diversity' // "limit_nlri_diversity": { // Type: schema.TypeInt, // Optional: true, // }, "limit_process_routes_ipv4": { Type: schema.TypeInt, Optional: true, Description: "Try to limit the amount of received IPv4 routes to the specified number. This number " + "does not represent the exact number of routes going to be installed in the routing " + "table by the peer. BGP session 'clear' command must be used to reset the flag if the " + "limit is reached.", }, "limit_process_routes_ipv6": { Type: schema.TypeInt, Optional: true, Description: "Try to limit the amount of received IPv6 routes to the specified number. This number " + "does not represent the exact number of routes going to be installed in the routing " + "table by the peer. BGP session 'clear' command must be used to reset the flag if the " + "limit is reached.", }, }, }, }, "instance": { Type: schema.TypeString, Optional: true, Description: "Name of the instance this VPN is assigned to.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "keepalive_time": { Type: schema.TypeString, Optional: true, Default: "3m", Description: "How long to keep the BGP session open after the last received 'keepalive' message.", DiffSuppressFunc: TimeEqual, }, "listen": { Type: schema.TypeBool, Optional: true, Default: true, Description: "Whether to listen for incoming connections.", }, "local": { Type: schema.TypeList, Optional: true, Description: "A group of parameters associated with BGP input.", MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "address": { Type: schema.TypeString, Optional: true, Description: "Local connection IPv4/6 address.", }, "default_address": { Type: schema.TypeString, Computed: true, Description: "", }, "port": { Type: schema.TypeInt, Optional: true, Default: 179, Description: "Local connection port.", ValidateFunc: Validation64k, }, "role": { Type: schema.TypeString, Required: true, Description: "BGP role, in most common scenarios it should be set to iBGP or eBGP. More " + "information on BGP roles can be found in the corresponding [RFC draft]" + "(https://datatracker.ietf.org/doc/draft-ietf-idr-bgp-open-policy/?include_text=1)", ValidateFunc: validation.StringInSlice( []string{ "ebgp", "ebgp-customer", "ebgp-peer", "ebgp-provider", "ebgp-rs", "ebgp-rs-client", "ibgp", "ibgp-rr", "ibgp-rr-client", }, false, ), }, "ttl": { Type: schema.TypeInt, Optional: true, Description: "Time To Live (hop limit) that will be recorded in sent TCP packets.", ValidateFunc: validation.IntBetween(1, 255), }, }, }, }, "multihop": { Type: schema.TypeBool, Optional: true, Description: "Specifies whether the remote peer is more than one hop away. This option affects " + "outgoing next-hop selection as described in RFC 4271 (for EBGP only, excluding EBGP " + "peers local to the confederation). It also affects: whether to accept connections from " + "peers that are not in the same network (the remote address of the connection is used " + "for this check); whether to accept incoming routes with NEXT_HOP attribute that is not " + "in the same network as the address used to establish the connection; the target-scope " + "of the routes installed from this peer; routes from multi-hop or IBGP peers resolve " + "their next-hops through IGP routes by default.", }, KeyName: PropName("Name of the BGP connection."), "nexthop_choice": { Type: schema.TypeString, Optional: true, Default: "default", Description: "Affects the outgoing NEXT_HOP attribute selection. Note that next-hops set in filters " + "always take precedence. Also note that the next-hop is not changed on route reflection, " + "except when it's set in the filter. default - select the next-hop as described in RFC " + "4271 force-self - always use a local address of the interface that is used to connect to " + "the peer as the next-hop; propagate - try to propagate further the next-hop received; " + "i.e. if the route has BGP NEXT_HOP attribute, then use it as the next-hop, otherwise, " + "fall back to the default case.", ValidateFunc: validation.StringInSlice([]string{"default", "force-self", "propagate"}, false), }, "output": { Type: schema.TypeList, Optional: true, Description: "A group of parameters associated with BGP output.", MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ // May be "0" ?!? // affinity (afi | alone | instance | main | remote-as | vrf; Default: ) "affinity": { Type: schema.TypeString, Optional: true, Description: "Configure output multicore processing. Read more in Routing Protocol Multi-core Support " + "article. alone - input and output of each session is processed in its own process, the " + "most likely best option when there are a lot of cores and a lot of peers afi, instance, " + "vrf, remote-as - try to run input/output of new session in process with similar " + "parameters main - run input/output in the main process (could potentially increase " + "performance on single-core even possibly on multicore devices with small amount of " + "cores) input - run output in the same process as input (can be set only for output " + "affinity).", }, "as_override": { Type: schema.TypeBool, Optional: true, Description: "If set, then all instances of the remote peer's AS number in the BGP AS-PATH attribute " + "are replaced with the local AS number before sending a route update to that peer. " + "Happens before routing filters and prepending.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "default_originate": { Type: schema.TypeString, Optional: true, Description: "Specifies default route (0.0.0.0/0) distribution method.", ValidateFunc: validation.StringInSlice([]string{"always", "if-installed", "never"}, false), }, "default_prepend": { Type: schema.TypeInt, Optional: true, Description: "The count of AS prepended to the AS path.", ValidateFunc: validation.IntBetween(0, 255), }, "filter_chain": { Type: schema.TypeString, Optional: true, Description: "Name of the routing filter chain to be used on the output prefixes. If the chain is " + "not specified, then BGP by default accepts everything.", }, "filter_select": { Type: schema.TypeString, Optional: true, Description: "Name of the routing select chain to be used for prefix selection. If not specified, then " + "default selection is used.", }, "keep_sent_attributes": { Type: schema.TypeBool, Optional: true, Description: "Store in memory sent prefix attributes, required for ' dump-saved-advertisements ' " + "command to work. By default, sent-out prefixes are not stored to preserve the router's " + "memory. An option should be enabled only for debugging purposes when necessary to see " + "currently advertised prefixes.", }, "network": { Type: schema.TypeString, Optional: true, Description: "Name of the address list used to send local networks. The network is sent only if a " + "matching IGP route exists in the routing table.", }, "no_client_to_client_reflection": { Type: schema.TypeBool, Optional: true, Description: "Disable client-to-client route reflection in Route Reflector setups.", }, "no_early_cut": { Type: schema.TypeBool, Optional: true, Description: "The early cut is the mechanism, to guess (based on default RFC behavior) what would " + "happen with the sent NPLRI when received by the remote peer. If the algorithm " + "determines that the NLRI is going to be dropped, a peer will not even try to send it. " + "However such behavior may not be desired in specific scenarios, then then this option " + "should be used to disable the early cut feature.", }, "redistribute": { Type: schema.TypeString, Optional: true, Description: "Enable redistribution of specified route types.", ValidateDiagFunc: ValidationMultiValInSlice([]string{ "bgp", "connected", "bgp-mpls-vpn", "dhcp", "fantasy", "modem", "ospf", "rip", "static", "vpn", }, false, false), }, "remove_private_as": { Type: schema.TypeBool, Optional: true, Description: "If set, then the BGP AS-PATH attribute is removed before sending out route updates if " + "the attribute contains only private AS numbers. The removal process happens before " + "routing filters are applied and before the local, AS number is prepended to the AS path.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, }, }, }, "remote": { Type: schema.TypeList, Optional: true, Description: "A group of parameters associated with BGP input.", MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "address": { Type: schema.TypeString, Optional: true, Description: "Remote IPv4/6 address used to connect and/or listen to.", }, "allowed_as": { Type: schema.TypeString, Optional: true, Description: "List of remote AS numbers that are allowed to connect. Useful for dynamic peer " + "configuration.", }, "as": { Type: schema.TypeString, Optional: true, Description: "Remote AS number. If not specified BGP will determine remote AS automatically " + "from the OPEN message.", }, "port": { Type: schema.TypeInt, Optional: true, Description: "Local connection port.", Default: 179, ValidateFunc: Validation64k, }, "ttl": { Type: schema.TypeInt, Optional: true, Description: "Acceptable minimum Time To Live, the hop limit for this TCP connection. For " + "example, if 'ttl=255' then only single-hop neighbors will be able to establish the " + "connection. This property only affects EBGP peers.", ValidateFunc: validation.IntBetween(1, 255), }, }, }, }, "router_id": { Type: schema.TypeString, Optional: true, Description: "BGP Router ID to be used. Use the ID from the /routing/router-id configuration by " + "specifying the reference name, or set the ID directly by specifying IP. Equal " + "router-ids are also used to group peers into one instance.", Deprecated: DeprecatedInfo("7.20"), }, "routing_table": { Type: schema.TypeString, Optional: true, Computed: true, Description: "Name of the routing table, to install routes in.", }, "save_to": { Type: schema.TypeString, Optional: true, Description: "Filename to be used to save BGP protocol-specific packet content (Exported PDU) into " + "pcap file. This method allows much simpler peer-specific packet capturing for debugging " + "purposes. Pcap files in this format can also be loaded to create virtual BGP peers to " + "recreate conditions that happened at the time when packet capture was running.", }, "tcp_md5_key": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "The key used to authenticate the connection with TCP MD5 signature as described in RFC 2385. " + "If not specified, authentication is not used.", }, "templates": { Type: schema.TypeSet, Optional: true, Description: "List of the template names, to inherit parameters from. Useful for dynamic BGP peers.", Elem: &schema.Schema{ Type: schema.TypeString, }, }, "use_bfd": { Type: schema.TypeBool, Optional: true, Description: "Whether to use the BFD protocol for faster connection state detection.", }, KeyVrf: PropVrfRw, } return &schema.Resource{ Description: "> [!WARNING] Using this resource you may happen unexpected behavior, for example, some of the attributes " + "may not be removable after adding them to the TF configuration. Please report this to GitHub and it " + "may be possible to fix it. Use the resource at your own risk as it is!", CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_routing_bgp_connection_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testBGPConnectionMinVersion = "7.12" const testBGPConnectionAddress = "routeros_routing_bgp_connection.test" func TestAccBGPConnectionTest_basic(t *testing.T) { if !testCheckMinVersion(t, testBGPConnectionMinVersion) { t.Logf("Test skipped, the minimum required version is %v", testBGPConnectionMinVersion) return } for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/routing/bgp/connection", "routeros_routing_bgp_connection"), Steps: []resource.TestStep{ { Config: testAccBGPConnectionConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testBGPConnectionAddress), resource.TestCheckResourceAttr(testBGPConnectionAddress, "name", "neighbor-test"), ), }, }, }) }) } } func testAccBGPConnectionConfig() string { return providerConfig + ` resource "routeros_routing_bgp_connection" "test" { add_path_out = "none" address_families = "ip" as = "65550/5" cisco_vpls_nlri_len_fmt = "auto-bits" cluster_id = "0.0.0.0" connect = true hold_time = "infinity" input { accept_communities = "111" accept_ext_communities = "222" accept_large_communities = "444" accept_nlri = "" accept_unknown = "" affinity = "alone" allow_as = "0" filter = "" ignore_as_path_len = true limit_process_routes_ipv4 = 5 limit_process_routes_ipv6 = 2 } keepalive_time = "4m" listen = true local { address = "127.0.0.1" port = 22334 role = "ebgp" ttl = "5" } multihop = true name = "neighbor-test" nexthop_choice = "default" output { affinity = "alone" as_override = true default_originate = "never" default_prepend = "1" filter_chain = "" filter_select = "" keep_sent_attributes = true network = "" no_client_to_client_reflection = true no_early_cut = true redistribute = "rip,bgp" remove_private_as = true } remote { address = "172.17.0.1" allowed_as = "1111" as = "12345/5" port = "11223" ttl = "5" } router_id = "0.0.0.1" routing_table = "main" save_to = "bgp.dump" tcp_md5_key = "poipoipoipoipoi" templates = [] use_bfd = "true" vrf = "main" } ` } ================================================ FILE: routeros/resource_routing_bgp_evpn.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* [ { ".about": "rd must be specified for bundled vlans", ".id": "*1", "export.route-targets": "1010:1010,1010:1020", "instance": "test", "name": "test" } ] */ // https://help.mikrotik.com/docs/display/ROS/ func ResourceRoutingBgpEvpn() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/routing/bgp/evpn"), MetaId: PropId(Id), KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "instance": { Type: schema.TypeString, Required: true, Description: "BGP instance this EVPN is assigned to.", }, "export": { Type: schema.TypeList, Optional: true, Description: "A group of parameters associated with the route export.", MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "route_targets": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "List of route targets that will be added to EVPN routes when exporting.", }, }, }, }, "import": { Type: schema.TypeList, Optional: true, Description: "A group of parameters associated with the route import.", MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "route_targets": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "List of route targets that will be used to import EVPN routes.", }, }, }, }, KeyName: PropName("Name of the entry."), "rd": { Type: schema.TypeString, Optional: true, Description: "Specifies the value that gets attached to route so that receiving routers can distinguish " + "advertisements that may otherwise look the same. Used to distinguish between tenants using overlapping " + "IP ranges. Also can be used to simplify convergence and redundancy within Virtual Network. RDs form " + "MLAG pairs should be unique, too.", }, "vni": { Type: schema.TypeInt, Optional: true, Description: "Range of Virtual Network Identifiers.", }, "vrf": { Type: schema.TypeString, Optional: true, Description: "VRF name.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_routing_bgp_evpn_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testBgpEvpnMinVersion = "7.20" const testBgpEvpn = "routeros_routing_bgp_evpn.test" func TestAccBgpEvpnTest_basic(t *testing.T) { if !testCheckMinVersion(t, testBgpEvpnMinVersion) { t.Logf("Test skipped, the minimum required version is %v", testBgpEvpnMinVersion) return } t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/routing/bgp/evpn", "routeros_routing_bgp_evpn"), Steps: []resource.TestStep{ { Config: testAccBgpEvpnConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testBgpEvpn), resource.TestCheckResourceAttr(testBgpEvpn, "name", "bgp-evpn-1"), resource.TestCheckResourceAttr(testBgpEvpn, "instance", "bgp-instance-1"), resource.TestCheckResourceAttr(testBgpEvpn, "vni", "1010"), ), }, }, }) }) } } func testAccBgpEvpnConfig() string { return fmt.Sprintf(`%v resource "routeros_routing_bgp_instance" "test" { as = "65000" name = "bgp-instance-1" } resource "routeros_routing_bgp_evpn" "test" { disabled = false export { route_targets = ["1010:1010"] } import { route_targets = ["1010:1010"] } instance = resource.routeros_routing_bgp_instance.test.name name = "bgp-evpn-1" vni = 1010 } `, providerConfig) } ================================================ FILE: routeros/resource_routing_bgp_instance.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*2", "as": "65535", "disabled": "false", "inactive": "false", "name": "bgp-instance-1" } */ // https://help.mikrotik.com/docs/display/ROS/ func ResourceRoutingBgpInstance() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/routing/bgp/instance"), MetaId: PropId(Id), "as": { Type: schema.TypeString, Optional: true, Description: "32-bit BGP autonomous system number. Value can be entered in AS-Plain and AS-Dot formats. " + "The parameter is also used to set up the BGP confederation, in the following format: confederation_as/as. " + "For example, if your AS is 34 and your confederation AS is 43, then as configuration should be as=43/34.", }, "cluster_id": { Type: schema.TypeString, Optional: true, Description: "In case this instance is a route reflector: the cluster ID of the router reflector cluster " + "to this instance belongs. This attribute helps to recognize routing updates that come from another route " + "reflector in this cluster and avoid routing information looping. Note that normally there is only one " + "route reflector in a cluster; in this case, `cluster-id` does not need to be configured and BGP router " + "ID is used instead.", }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "ignore_as_path_len": { Type: schema.TypeBool, Optional: true, Description: "Whether to ignore the `AS_PATH` attribute in the BGP route selection algorithm. Works on input.", }, KeyInactive: PropInactiveRo, KeyName: PropName("Instance name."), "router_id": { Type: schema.TypeString, Optional: true, Description: "BGP Router ID to be used. Use the ID from the `/routing/router-id` configuration by specifying " + "the reference name, or set the ID directly by specifying IP.Equal router-ids are also used to group " + "peers into one instance.", }, "routing_table": { Type: schema.TypeString, Optional: true, Description: "Name of the routing table, to install routes in.", }, "vrf": { Type: schema.TypeString, Optional: true, Description: "Name of the VRF BGP connections operates on. By default always use the `main` routing table.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_routing_bgp_instance_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testBgpInstanceMinVersion = "7.20" const testRoutingBgpInstance = "routeros_routing_bgp_instance.test" func TestAccRoutingBgpInstanceTest_basic(t *testing.T) { if !testCheckMinVersion(t, testBgpInstanceMinVersion) { t.Logf("Test skipped, the minimum required version is %v", testBgpInstanceMinVersion) return } t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/routing/bgp/instance", "routeros_routing_bgp_instance"), Steps: []resource.TestStep{ { Config: testAccRoutingBgpInstanceConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testRoutingBgpInstance), resource.TestCheckResourceAttr(testRoutingBgpInstance, "as", "65000"), resource.TestCheckResourceAttr(testRoutingBgpInstance, "name", "bgp-instance-1"), ), }, }, }) }) } } func testAccRoutingBgpInstanceConfig() string { return fmt.Sprintf(`%v resource "routeros_routing_bgp_instance" "test" { as = "65000" name = "bgp-instance-1" } `, providerConfig) } ================================================ FILE: routeros/resource_routing_bgp_template.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".about": "invalid value '0.0.0.0' of 'router-id'", ".id": "*2", "add-path-out": "all", "address-families": "ip,ipv6,l2vpn,l2vpn-cisco,vpnv4", "as": "65000", "as-override": "true", "cisco-vpls-nlri-len-fmt": "auto-bits", "cluster-id": "0.0.0.0", "comment": "tmpl", "disabled": "false", "hold-time": "infinity", "inactive": "true", "input.accept-communities": "", "input.accept-ext-communities": "", "input.accept-large-communities": "", "input.accept-nlri": "", "input.accept-unknown": "", "input.affinity": "0", "input.allow-as": "0", "input.filter": "", "input.ignore-as-path-len": "true", "keepalive-time": "1s", "multihop": "true", "name": "temp1", "nexthop-choice": "default", "output.affinity": "0", "output.default-originate": "never", "output.default-prepend": "0", "output.filter-chain": "", "output.filter-select": "", "output.keep-sent-attributes": "true", "output.network": "", "output.no-client-to-client-reflection": "true", "output.no-early-cut": "true", "output.redistribute": "connected,static,rip,ospf,bgp,vpn,dhcp,fantasy,modem,copy", "remove-private-as": "true", "router-id": "0.0.0.0", "routing-table": "main", "templates": "default", "use-bfd": "true", "vrf": "main" } */ // https://help.mikrotik.com/docs/display/ROS/ func ResourceRoutingBgpTemplate() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/routing/bgp/template"), MetaId: PropId(Id), "add_path_out": { Type: schema.TypeString, Optional: true, Description: "", Default: "none", ValidateFunc: validation.StringInSlice([]string{"all", "none"}, false), }, "address_families": { Type: schema.TypeString, Optional: true, Default: "ip", Description: "List of address families about which this peer will exchange routing information. The " + "remote peer must support (they usually do) BGP capabilities optional parameter to " + "negotiate any other families than IP.", ValidateDiagFunc: ValidationMultiValInSlice([]string{"ip", "ipv6", "l2vpn", "l2vpn-cisco", "vpnv4"}, false, false), }, "as": { Type: schema.TypeString, Required: true, Description: "32-bit BGP autonomous system number. Value can be entered in AS-Plain and AS-Dot " + "formats. The parameter is also used to set up the BGP confederation, in the following " + "format: confederation_as/as . For example, if your AS is 34 and your confederation AS is " + "43, then as configuration should be as =43/34.", }, "as_override": { Type: schema.TypeBool, Optional: true, Description: "If set, then all instances of the remote peer's AS number in the BGP AS-PATH attribute " + "are replaced with the local AS number before sending a route update to that peer. " + "Happens before routing filters and prepending.", }, "cisco_vpls_nlri_len_fmt": { Type: schema.TypeString, Optional: true, Description: "VPLS NLRI length format type. Used for compatibility with Cisco VPLS.", ValidateFunc: validation.StringInSlice([]string{"auto-bits", "auto-bytes", "bits", "bytes"}, false), }, "cluster_id": { Type: schema.TypeString, Optional: true, Description: "In case this instance is a route reflector: the cluster ID of the router reflector " + "cluster to this instance belongs. This attribute helps to recognize routing updates " + "that come from another route reflector in this cluster and avoid routing information " + "looping. Note that normally there is only one route reflector in a cluster; in this " + "case, 'cluster-id' does not need to be configured and BGP router ID is used instead.", ValidateFunc: validation.IsIPv4Address, Deprecated: DeprecatedInfo("7.20"), }, KeyComment: PropCommentRw, KeyDefault: PropDefaultRo, KeyDisabled: PropDisabledRw, // hold-time ( time[3s..1h] | infinity ; Default: 3m ) "hold_time": { Type: schema.TypeString, Optional: true, Computed: true, Description: "Specifies the BGP Hold Time value to use when negotiating with peers. According to the " + "BGP specification, if the router does not receive successive KEEPALIVE and/or UPDATE " + "and/or NOTIFICATION messages within the period specified in the Hold Time field of the " + "OPEN message, then the BGP connection to the peer will be closed. The minimal hold-time " + "value of both peers will be actually used (note that the special value 0 or 'infinity' " + "is lower than any other value) infinity - never expire the connection and never send " + "keepalive messages.", }, "input": { Type: schema.TypeList, Optional: true, Description: "A group of parameters associated with BGP input.", MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "accept_comunities": { Type: schema.TypeString, Optional: true, Description: "A quick way to filter incoming updates with specific communities. It allows filtering " + "incoming messages directly before they are even parsed and stored in memory, that way " + "significantly reducing memory usage. Regular input filter chain can only reject " + "prefixes which means that it will still eat memory and will be visible in /routing " + "route table as 'not active, filtered'. Changes to be applied required session refresh.", }, "accept_ext_communities": { Type: schema.TypeString, Optional: true, Description: "A quick way to filter incoming updates with specific extended communities. It allows " + "filtering incoming messages directly before they are even parsed and stored in memory, " + "that way significantly reducing memory usage. Regular input filter chain can only " + "reject prefixes which means that it will still eat memory and will be visible in " + "/routing route table as 'not active, filtered'. Changes to be applied required session " + "refresh.", }, "accept_large_comunities": { Type: schema.TypeString, Optional: true, Description: "A quick way to filter incoming updates with specific large communities. It allows " + "filtering incoming messages directly before they are even parsed and stored in memory, " + "that way significantly reducing memory usage. Regular input filter chain can only " + "reject prefixes which means that it will still eat memory and will be visible in " + "/routing route table as 'not active, filtered'. Changes to be applied required session " + "refresh.", }, "accept_nlri": { Type: schema.TypeString, Optional: true, Description: "Name of the ipv4/6 address-list. A quick way to filter incoming updates with specific " + "NLRIs. It allows filtering incoming messages directly before they are even parsed and " + "stored in memory, that way significantly reducing memory usage. Regular input filter " + "chain can only reject prefixes which means that it will still eat memory and will be " + "visible in /routing route table as 'not active, filtered'. Changes to be applied " + "required session restart.", }, "accept_unknown": { Type: schema.TypeString, Optional: true, Description: "A quick way to filter incoming updates with specific 'unknown' attributes. It allows " + "filtering incoming messages directly before they are even parsed and stored in memory, " + "that way significantly reducing memory usage. Regular input filter chain can only " + "reject prefixes which means that it will still eat memory and will be visible in " + "/routing route table as 'not active, filtered'. Changes to be applied required session " + "refresh.", Deprecated: DeprecatedInfo("7.19"), }, // affinity (afi | alone | instance | main | remote-as | vrf; Default: ) // May be "0" "affinity": { Type: schema.TypeString, Optional: true, Description: "Configure input multi-core processing. Read more in Routing Protocol Multi-core Support " + "article. alone - input and output of each session are processed in its own process, " + "most likely the best option when there are a lot of cores and a lot of peers afi, " + "instance, vrf, remote-as - try to run input/output of new session in process with " + "similar parameters main - run input/output in the main process (could potentially " + "increase performance on single-core even possibly on multi-core devices with a small " + "amount of cores) input - run output in the same process as input (can be set only for " + "output affinity)", }, "allow_as": { Type: schema.TypeInt, Optional: true, Description: "Indicates how many times to allow your own AS number in AS-PATH, before discarding a " + "prefix.", ValidateFunc: validation.IntBetween(0, 10), }, "filter": { Type: schema.TypeString, Optional: true, Description: "Name of the routing filter chain to be used on input prefixes. This happens after " + "NLRIs are processed. If the chain is not specified, then BGP by default accepts " + "everything.", }, "filter_communities": { Type: schema.TypeString, Optional: true, Description: "A quick way to filter incoming updates with specific communities. It allows filtering " + "incoming messages directly before they are even parsed and stored in memory, that way significantly " + "reducing memory usage. Regular input filter chain can only reject prefixes which means that it will " + "still eat memory and will be visible in /routing route table as \"not active, filtered\". Changes " + "to be applied required session refresh.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "filter_ext_communities": { Type: schema.TypeString, Optional: true, Description: "A quick way to filter incoming updates with specific extended communities. It allows filtering " + "incoming messages directly before they are even parsed and stored in memory, that way significantly " + "reducing memory usage. Regular input filter chain can only reject prefixes which means that it will " + "still eat memory and will be visible in /routing route table as \"not active, filtered\". Changes " + "to be applied required session refresh.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "filter_large_communities": { Type: schema.TypeString, Optional: true, Description: "A quick way to filter incoming updates with specific large communities. It allows filtering " + "incoming messages directly before they are even parsed and stored in memory, that way significantly " + "reducing memory usage. Regular input filter chain can only reject prefixes which means that it will " + "still eat memory and will be visible in /routing route table as \"not active, filtered\". Changes " + "to be applied required session refresh.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "filter_nlri": { Type: schema.TypeString, Optional: true, Description: "Name of the filter chain that will filter incoming IPv4/IPv6 NLRIs directly before " + "they are stored in memory, that way significantly reducing memory usage. Regular input filter " + "chain can only reject prefixes which means that it will still eat memory and will be visible in " + "`/routing route` table as `not active, filtered`. Changes to be applied required session restart.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "filter_unknown": { Type: schema.TypeString, Optional: true, Description: "A quick way to filter incoming updates with specific \"unknown\" attributes. It allows filtering " + "incoming messages directly before they are even parsed and stored in memory, that way significantly " + "reducing memory usage. Regular input filter chain can only reject prefixes which means that it will " + "still eat memory and will be visible in /routing route table as \"not active, filtered\". Changes " + "to be applied required session refresh.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "ignore_as_path_len": { Type: schema.TypeBool, Optional: true, Description: "Whether to ignore the AS_PATH attribute in the BGP route selection algorithm", Deprecated: DeprecatedInfo("7.20"), }, // FIXME ROS 7.8: 'unknown parameter input.limit-nlri-diversity' // "limit_nlri_diversity": { // Type: schema.TypeInt, // Optional: true, // }, "limit_process_routes_ipv4": { Type: schema.TypeInt, Optional: true, Description: "Try to limit the amount of received IPv4 routes to the specified number. This number " + "does not represent the exact number of routes going to be installed in the routing " + "table by the peer. BGP session 'clear' command must be used to reset the flag if the " + "limit is reached.", }, "limit_process_routes_ipv6": { Type: schema.TypeInt, Optional: true, Description: "Try to limit the amount of received IPv6 routes to the specified number. This number " + "does not represent the exact number of routes going to be installed in the routing " + "table by the peer. BGP session 'clear' command must be used to reset the flag if the " + "limit is reached.", }, }, }, }, "keepalive_time": { Type: schema.TypeString, Optional: true, Default: "3m", Description: "How long to keep the BGP session open after the last received 'keepalive' message.", DiffSuppressFunc: TimeEqual, }, "multihop": { Type: schema.TypeBool, Optional: true, Description: "Specifies whether the remote peer is more than one hop away. This option affects " + "outgoing next-hop selection as described in RFC 4271 (for EBGP only, excluding EBGP " + "peers local to the confederation). It also affects: whether to accept connections from " + "peers that are not in the same network (the remote address of the connection is used " + "for this check); whether to accept incoming routes with NEXT_HOP attribute that is not " + "in the same network as the address used to establish the connection; the target-scope " + "of the routes installed from this peer; routes from multi-hop or IBGP peers resolve " + "their next-hops through IGP routes by default.", }, KeyName: PropName("Name of the BGP template."), "nexthop_choice": { Type: schema.TypeString, Optional: true, Default: "default", Description: "Affects the outgoing NEXT_HOP attribute selection. Note that next-hops set in filters " + "always take precedence. Also note that the next-hop is not changed on route reflection, " + "except when it's set in the filter. default - select the next-hop as described in RFC " + "4271 force-self - always use a local address of the interface that is used to connect to " + "the peer as the next-hop; propagate - try to propagate further the next-hop received; " + "i.e. if the route has BGP NEXT_HOP attribute, then use it as the next-hop, otherwise, " + "fall back to the default case.", ValidateFunc: validation.StringInSlice([]string{"default", "force-self", "propagate"}, false), }, "output": { Type: schema.TypeList, Optional: true, Description: "A group of parameters associated with BGP output.", MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ // May be "0" ?!? // affinity (afi | alone | instance | main | remote-as | vrf; Default: ) "affinity": { Type: schema.TypeString, Optional: true, Description: "Configure output multicore processing. Read more in Routing Protocol Multi-core Support " + "article. alone - input and output of each session is processed in its own process, the " + "most likely best option when there are a lot of cores and a lot of peers afi, instance, " + "vrf, remote-as - try to run input/output of new session in process with similar " + "parameters main - run input/output in the main process (could potentially increase " + "performance on single-core even possibly on multicore devices with small amount of " + "cores) input - run output in the same process as input (can be set only for output " + "affinity).", }, "default_originate": { Type: schema.TypeString, Optional: true, Description: "Specifies default route (0.0.0.0/0) distribution method.", ValidateFunc: validation.StringInSlice([]string{"always", "if-installed", "never"}, false), }, "default_prepend": { Type: schema.TypeInt, Optional: true, Description: "The count of AS prepended to the AS path.", ValidateFunc: validation.IntBetween(0, 255), }, "filter_chain": { Type: schema.TypeString, Optional: true, Description: "Name of the routing filter chain to be used on the output prefixes. If the chain is " + "not specified, then BGP by default accepts everything.", }, "filter_select": { Type: schema.TypeString, Optional: true, Description: "Name of the routing select chain to be used for prefix selection. If not specified, then " + "default selection is used.", }, "keep_sent_attributes": { Type: schema.TypeBool, Optional: true, Description: "Store in memory sent prefix attributes, required for ' dump-saved-advertisements ' " + "command to work. By default, sent-out prefixes are not stored to preserve the router's " + "memory. An option should be enabled only for debugging purposes when necessary to see " + "currently advertised prefixes.", }, "network": { Type: schema.TypeString, Optional: true, Description: "Name of the address list used to send local networks. The network is sent only if a " + "matching IGP route exists in the routing table.", }, "no_client_to_client_reflection": { Type: schema.TypeBool, Optional: true, Description: "Disable client-to-client route reflection in Route Reflector setups.", }, "no_early_cut": { Type: schema.TypeBool, Optional: true, Description: "The early cut is the mechanism, to guess (based on default RFC behavior) what would " + "happen with the sent NPLRI when received by the remote peer. If the algorithm " + "determines that the NLRI is going to be dropped, a peer will not even try to send it. " + "However such behavior may not be desired in specific scenarios, then then this option " + "should be used to disable the early cut feature.", }, "redistribute": { Type: schema.TypeString, Optional: true, Description: "Enable redistribution of specified route types.", ValidateDiagFunc: ValidationMultiValInSlice([]string{ "bgp", "connected", "bgp-mpls-vpn", "dhcp", "fantasy", "modem", "ospf", "rip", "static", "vpn", }, false, false), }, }, }, }, "remove_private_as": { Type: schema.TypeBool, Optional: true, Description: "If set, then the BGP AS-PATH attribute is removed before sending out route updates if " + "the attribute contains only private AS numbers. The removal process happens before " + "routing filters are applied and before the local, AS number is prepended to the AS path.", }, "router_id": { Type: schema.TypeString, Optional: true, Description: "BGP Router ID to be used. Use the ID from the /routing/router-id configuration by " + "specifying the reference name, or set the ID directly by specifying IP. Equal " + "router-ids are also used to group peers into one instance.", Deprecated: DeprecatedInfo("7.20"), }, "routing_table": { Type: schema.TypeString, Optional: true, Computed: true, Description: "Name of the routing table, to install routes in.", }, "save_to": { Type: schema.TypeString, Optional: true, Description: "Filename to be used to save BGP protocol-specific packet content (Exported PDU) into " + "pcap file. This method allows much simpler peer-specific packet capturing for debugging " + "purposes. Pcap files in this format can also be loaded to create virtual BGP peers to " + "recreate conditions that happened at the time when packet capture was running.", }, "templates": { Type: schema.TypeSet, Optional: true, Description: "List of template names from which to inherit parameters. Useful feature, to easily " + "configure groups with overlapping configuration options.", Elem: &schema.Schema{ Type: schema.TypeString, }, }, "use_bfd": { Type: schema.TypeBool, Optional: true, Description: "Whether to use the BFD protocol for faster connection state detection.", }, KeyVrf: PropVrfRw, } return &schema.Resource{ Description: "> [!WARNING] Using this resource you may happen unexpected behavior, for example, some of the attributes " + "may not be removable after adding them to the TF configuration. Please report this to GitHub and it " + "may be possible to fix it. Use the resource at your own risk as it is!", CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_routing_bgp_template_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testBGPTemplateAddress = "routeros_routing_bgp_template.test" func TestAccBGPTemplateTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/routing/bgp/template", "routeros_routing_bgp_template"), Steps: []resource.TestStep{ { Config: testAccBGPTemplateConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testBGPTemplateAddress), resource.TestCheckResourceAttr(testBGPTemplateAddress, "name", "test-template"), ), }, }, }) }) } } func testAccBGPTemplateConfig() string { return providerConfig + ` resource "routeros_routing_bgp_template" "test" { name = "test-template" as = 65521 input { limit_process_routes_ipv4 = 5 limit_process_routes_ipv6 = 5 } output { affinity = "alone" keep_sent_attributes = true default_originate = "never" } // save_to = "bgp.dump" } ` } ================================================ FILE: routeros/resource_routing_bgp_vpn.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".about": "main vrf not suitable for export", ".id": "*1", "disabled": "false", "inactive": "false", "label-allocation-policy": "per-prefix", "name": "bgp-mpls-vpn-1", "route-distinguisher": "123", "vrf": "main" } */ // https://help.mikrotik.com/docs/spaces/ROS/pages/331612228/routing+bgp#id-/routing/bgp-/routing/bgp/vpn func ResourceRoutingBgpVpn() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/routing/bgp/vpn"), MetaId: PropId(Id), KeyDisabled: PropDisabledRw, "export": { Type: schema.TypeList, Optional: true, Description: "A group of parameters associated with the route export.", MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "filter_chain": { Type: schema.TypeString, Optional: true, Description: "The name of the routing filter chain that is used to filter prefixes before exporting.", }, "filter_select": { Type: schema.TypeString, Optional: true, Description: "The name of the select filter chain that is used to select prefixes to be exported exporting.", }, "redistribute": { Type: schema.TypeString, Optional: true, Description: "Enable redistribution of specified route types from VRF to VPNv4.", ValidateFunc: validation.StringInSlice([]string{"bgp", "connected", "dhcp", "fantasy", "modem", "ospf", "rip", "static", "vpn"}, false), }, "route_targets": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "List of route targets added when exporting VPNv4 routes. The accepted RT format is similar " + "to the one for Route Distinguishers.", }, }, }, }, "import": { Type: schema.TypeList, Optional: true, Description: "A group of parameters associated with the route import.", MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "filter_chain": { Type: schema.TypeString, Optional: true, Description: "The name of the routing filter chain that is used to filter prefixes during import.", }, "route_targets": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "List of route targets that will be used to import VPNv4 routes. The accepted RT format is " + "similar to the one for Route Distinguishers.", }, "router_id": { Type: schema.TypeString, Optional: true, Description: "The router ID of the BGP instance that will be used for the BGP best path selection algorithm.", }, }, }, }, KeyInactive: PropInactiveRo, "instance": { Type: schema.TypeString, Optional: true, Description: "Name of the instance this VPN is assigned to.", }, "label_allocation_policy": { Type: schema.TypeString, Optional: true, Description: "Label allocationpolicy.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.StringInSlice([]string{"per-prefix", "per-vrf"}, false), }, KeyName: PropName("VPN instance name."), "route_distinguisher": { Type: schema.TypeString, Required: true, Description: "Helps to distinguish between overlapping routes from multiple VRFs. Should be unique " + "per VRF. Accepts 3 types of formats.", }, KeyVrf: PropVrfRw, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_routing_bgp_vpn_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testRoutingBgpVpn = "routeros_routing_bgp_vpn.test" func TestAccRoutingBgpVpnTest_basic(t *testing.T) { t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/routing/bgp/vpn", "routeros_routing_bgp_vpn"), Steps: []resource.TestStep{ { Config: testAccRoutingBgpVpnConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testRoutingBgpVpn), resource.TestCheckResourceAttr(testRoutingBgpVpn, "disabled", "false"), resource.TestCheckResourceAttr(testRoutingBgpVpn, "label_allocation_policy", "per-vrf"), resource.TestCheckResourceAttr(testRoutingBgpVpn, "name", "bgp-mpls-vpn-test"), resource.TestCheckResourceAttr(testRoutingBgpVpn, "route_distinguisher", "1.2.3.4:1"), resource.TestCheckResourceAttr(testRoutingBgpVpn, "vrf", "main")), }, }, }) }) } } func testAccRoutingBgpVpnConfig() string { return fmt.Sprintf(`%v resource "routeros_routing_bgp_vpn" "test" { disabled = false export { redistribute = "connected" route_targets = ["1:1"] } import { route_targets = ["1:2"] } label_allocation_policy = "per-vrf" name = "bgp-mpls-vpn-test" route_distinguisher = "1.2.3.4:1" vrf = "main" } `, providerConfig) } ================================================ FILE: routeros/resource_routing_filter_rule.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*2", "chain": "123", "comment": "test", "disabled": "true", "inactive": "true", "rule": "if (active) {accept}" } */ // https://help.mikrotik.com/docs/display/ROS/Route+Selection+and+Filters func ResourceRoutingFilterRule() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/routing/filter/rule"), MetaId: PropId(Id), "chain": { Type: schema.TypeString, Required: true, Description: "Chain name.", }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, KeyInactive: PropInactiveRo, "rule": { Type: schema.TypeString, Required: true, Description: "Filter rule.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_routing_filter_rule_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testRoutingFilterRule = "routeros_routing_filter_rule.test" func TestAccRoutingFilterRuleTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/routing/filter/rule", "routeros_routing_filter_rule"), Steps: []resource.TestStep{ { Config: testAccRoutingFilterRuleConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testRoutingFilterRule), resource.TestCheckResourceAttr(testRoutingFilterRule, "chain", "testChain"), resource.TestCheckResourceAttr(testRoutingFilterRule, "rule", "if (dst in 192.168.1.0/24 && dst-len>24) {set distance +1; accept} else {set distance -1; accept}"), resource.TestCheckResourceAttr(testRoutingFilterRule, "comment", "comment"), resource.TestCheckResourceAttr(testRoutingFilterRule, "disabled", "true"), ), }, }, }) }) } } func testAccRoutingFilterRuleConfig() string { return providerConfig + ` resource "routeros_routing_filter_rule" "test" { chain = "testChain" rule = "if (dst in 192.168.1.0/24 && dst-len>24) {set distance +1; accept} else {set distance -1; accept}" comment = "comment" disabled = true } ` } ================================================ FILE: routeros/resource_routing_id.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "comment": "", "disabled": "false", "dynamic": "false", "dynamic-id": "", "id": "10.10.10.10", "inactive": "false", "name": "router-id-1", "select-dynamic-id": "any", "select-from-vrf": "main" } */ // https://help.mikrotik.com/docs/spaces/ROS/pages/30474294/routing+id func ResourceRoutingId() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/routing/id"), MetaId: PropId(Id), MetaTransformSet: PropTransformSet("router_id:id"), KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, "dynamic_id": { Type: schema.TypeString, Computed: true, Description: "Currently selected ID.", }, KeyInactive: PropInactiveRo, KeyName: PropName("Reference name."), "router_id": { Type: schema.TypeString, Optional: true, ValidateFunc: validation.IsIPAddress, Description: "Parameter to explicitly set the Router ID. If not specified, it can be elected from one " + "of the configured IP addresses on the router.", }, "select_dynamic_id": { Type: schema.TypeString, Optional: true, Description: "States what IP addresses to use for ID election.", ValidateFunc: validation.StringInSlice([]string{ "any", "lowest", "only-active", "only-loopback", "only-static", "only-vrf", }, false), }, "select_from_vrf": { Type: schema.TypeString, Optional: true, Description: "VRF from which to select IP addresses for the ID election.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_routing_id_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testRoutingId = "routeros_routing_id.test" func TestAccRoutingIdTest_basic(t *testing.T) { t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/routing/id", "routeros_routing_id"), Steps: []resource.TestStep{ { Config: testAccRoutingIdConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testRoutingId), resource.TestCheckResourceAttr(testRoutingId, "name", "router-id-test"), resource.TestCheckResourceAttr(testRoutingId, "router_id", "10.10.10.10"), resource.TestCheckResourceAttr(testRoutingId, "select_dynamic_id", "any"), ), }, }, }) }) } } func testAccRoutingIdConfig() string { return fmt.Sprintf(`%v resource "routeros_routing_id" "test" { name = "router-id-test" router_id = "10.10.10.10" select_dynamic_id = "any" } `, providerConfig) } ================================================ FILE: routeros/resource_routing_igmp_proxy_interface.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*1", "alternative-subnets": "", "disabled": "false", "inactive": "true", "interface": "lo", "threshold": "1", "upstream": "false" } */ // https://help.mikrotik.com/docs/spaces/ROS/pages/128221386/IGMP+Proxy func ResourceRoutingIgmpProxyInterface() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/routing/igmp-proxy/interface"), MetaId: PropId(Id), "alternative_subnets": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, Description: "By default, only packets from directly attached subnets are accepted. This parameter can be " + "used to specify a list of alternative valid packet source subnets, both for data or IGMP packets. Has " + "an effect only on the upstream interface. Should be used when the source of multicast data often is " + "in a different IP network.", }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, KeyInactive: PropInactiveRo, KeyInterface: PropInterfaceRw, "threshold": { Type: schema.TypeInt, Optional: true, Description: "Minimal TTL. Packets received with a lower TTL value are ignored.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "upstream": { Type: schema.TypeBool, Optional: true, Description: "The interface is called `upstream` if it's in the direction of the root of the multicast " + "tree. An IGMP forwarding router must have exactly one upstream interface configured. The upstream interface " + "is used to send out IGMP membership requests.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_routing_igmp_proxy_interface_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testRoutingIgmpProxyInterface = "routeros_routing_igmp_proxy_interface.test" func TestAccRoutingIgmpProxyInterfaceTest_basic(t *testing.T) { t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/routing/igmp-proxy/interface", "routeros_routing_igmp_proxy_interface"), Steps: []resource.TestStep{ { Config: testAccRoutingIgmpProxyInterfaceConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testRoutingIgmpProxyInterface), resource.TestCheckResourceAttr(testRoutingIgmpProxyInterface, "alternative_subnets.0", "0.0.0.1/32"), resource.TestCheckResourceAttr(testRoutingIgmpProxyInterface, "alternative_subnets.1", "0.0.0.2/32"), resource.TestCheckResourceAttr(testRoutingIgmpProxyInterface, "disabled", "true"), resource.TestCheckResourceAttr(testRoutingIgmpProxyInterface, "interface", "all"), resource.TestCheckResourceAttr(testRoutingIgmpProxyInterface, "threshold", "5"), ), }, }, }) }) } } func testAccRoutingIgmpProxyInterfaceConfig() string { return fmt.Sprintf(`%v resource "routeros_routing_igmp_proxy_interface" "test" { alternative_subnets = ["0.0.0.1/32", "0.0.0.2/32"] disabled = true interface = "all" threshold = 5 } `, providerConfig) } ================================================ FILE: routeros/resource_routing_ospf_area.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*54", "name": "", "area-id": "", "default-cost": "", "instance": "", "no-summaries": "", "nssa-translate": "", "type": "", } */ // ResourceRoutingOspfArea https://help.mikrotik.com/docs/display/ROS/OSPF func ResourceRoutingOspfArea() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/routing/ospf/area"), MetaId: PropId(Id), MetaSetUnsetFields: PropSetUnsetFields("no_summaries"), "area_id": { Type: schema.TypeString, Default: "0.0.0.0", Optional: true, Description: "OSPF area identifier.", }, KeyComment: PropCommentRw, "default_cost": { Type: schema.TypeInt, Optional: true, Description: "Default cost of injected LSAs into the area.", }, KeyDisabled: PropDisabledRw, KeyInactive: PropInactiveRo, "instance": { Type: schema.TypeString, Required: true, Description: "Name of the OSPF instance this area belongs to.", }, KeyName: PropNameForceNewRw, "no_summaries": { Type: schema.TypeBool, Optional: true, Description: "If set then the area will not flood summary LSAs in the stub area. " + "The correct value of this attribute may not be displayed in Winbox. " + "Please check the parameters in the console!", }, "nssa_translate": { Type: schema.TypeString, Optional: true, Description: "The parameter indicates which ABR will be used as a translator from type7 to type5 LSA.", ValidateFunc: validation.StringInSlice([]string{"no", "yes", "candidate"}, false), }, "type": { Type: schema.TypeString, Optional: true, Default: "default", Description: "The area type.", ValidateFunc: validation.StringInSlice([]string{"default", "nssa", "stub"}, false), }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_routing_ospf_area_range.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "advertise": "true", "area": "ospf-area-1", "cost": "100", "disabled": "false", "inactive": "false", "prefix": "::/0" } */ // https://help.mikrotik.com/docs/spaces/ROS/pages/331612216/routing+ospf#id-/routing/ospf-/routing/ospf/area/range func ResourceRoutingOspfAreaRange() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/routing/ospf/area/range"), MetaId: PropId(Id), "advertise": { Type: schema.TypeBool, Optional: true, Description: "Whether to create a summary LSA and advertise it to the adjacent areas.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "area": { Type: schema.TypeString, Required: true, Description: "The OSPF area associated with this range.", }, KeyComment: PropCommentRw, "cost": { Type: schema.TypeInt, Optional: true, Description: "The cost of the summary LSA this range will createdefault - use the largest cost of all routes " + "used (i.e. routes that fall within this range).", }, KeyDisabled: PropDisabledRw, "prefix": { Type: schema.TypeString, Optional: true, Description: "The network prefix of this range.", ValidateFunc: validation.IsCIDR, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_routing_ospf_area_range_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testRoutingOspfAreaRange = "routeros_routing_ospf_area_range.test" func TestAccRoutingOspfAreaRangeTest_basic(t *testing.T) { t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/routing/ospf/area/range", "routeros_routing_ospf_area_range"), Steps: []resource.TestStep{ { Config: testAccRoutingOspfAreaRangeConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testRoutingOspfAreaRange), resource.TestCheckResourceAttr(testRoutingOspfAreaRange, "area", "ospf-area-oar"), resource.TestCheckResourceAttr(testRoutingOspfAreaRange, "advertise", "true"), resource.TestCheckResourceAttr(testRoutingOspfAreaRange, "prefix", "::/64"), resource.TestCheckResourceAttr(testRoutingOspfAreaRange, "disabled", "true"), ), }, }, }) }) } } func testAccRoutingOspfAreaRangeConfig() string { return fmt.Sprintf(`%v resource "routeros_routing_ospf_instance" "ospf-instance-oar" { name = "ospf-instance-oar" disabled = false } resource "routeros_routing_ospf_area" "ospf-area-oar" { name = "ospf-area-oar" disabled = true instance = routeros_routing_ospf_instance.ospf-instance-oar.name } resource "routeros_routing_ospf_area_range" "test" { area = routeros_routing_ospf_area.ospf-area-oar.name advertise = true prefix = "::/64" disabled = true } `, providerConfig) } ================================================ FILE: routeros/resource_routing_ospf_area_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testRoutingOspfArea = "routeros_routing_ospf_area.test_routing_ospf_area" func TestAccRoutingOspfInstanceArea_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/routing/ospf/area", "routeros_routing_ospf_area"), Steps: []resource.TestStep{ { Config: testAccCheckRoutingOspfAreaConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testRoutingOspfArea), resource.TestCheckResourceAttr(testRoutingOspfArea, "name", "test_routing_ospf_area"), ), }, }, }) }) } } func testAccCheckRoutingOspfAreaConfig() string { return providerConfig + ` resource "routeros_routing_ospf_instance" "test_routing_ospf_instance" { name = "test_routing_ospf_instance" disabled = false } resource "routeros_routing_ospf_area" "test_routing_ospf_area" { name = "test_routing_ospf_area" disabled = true instance = routeros_routing_ospf_instance.test_routing_ospf_instance.name } ` } ================================================ FILE: routeros/resource_routing_ospf_instance.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*54", "name": "", "domain-id": "", "domain-tag": "", "in-filter": "", "mpls-te-address": "", "mpls-te-area": "", "originate-default": "", "out-filter-chain": "", "out-filter-select": "", "redistribute": "", "router-id": "", "version": "", "vrf": "", "use-dn": "" // is not present in version 7.9 } */ // ResourceRoutingOspfInstance https://help.mikrotik.com/docs/display/ROS/OSPF func ResourceRoutingOspfInstance() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/routing/ospf/instance"), MetaId: PropId(Id), KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "domain_id": { Type: schema.TypeString, Optional: true, Description: "MPLS-related parameter.", }, "domain_tag": { Type: schema.TypeInt, Optional: true, Description: "if set, then used in route redistribution (as route-tag in all external LSAs " + "generated by this router), and in route calculation (all external LSAs having this route " + "tag are ignored). Needed for interoperability with older Cisco systems. By default not set.", }, KeyInactive: PropInactiveRo, "in_filter_chain": { Type: schema.TypeString, Optional: true, Description: "name of the routing filter chain used for incoming prefixes", }, "mpls_te_address": { Type: schema.TypeString, Optional: true, Description: "the area used for MPLS traffic engineering.", }, "mpls_te_area": { Type: schema.TypeString, Optional: true, Description: "the area used for MPLS traffic engineering.", }, KeyName: PropNameForceNewRw, "originate_default": { Type: schema.TypeString, Optional: true, Description: "Specifies default route (0.0.0.0/0) distribution method.", ValidateFunc: validation.StringInSlice([]string{"always", "if-installed", "never"}, false), }, "out_filter_chain": { Type: schema.TypeString, Optional: true, Description: "name of the routing filter chain used for outgoing prefixes filtering.", }, "out_filter_select": { Type: schema.TypeString, Optional: true, Description: "name of the routing filter select chain, used for output selection.", }, "redistribute": { Type: schema.TypeSet, Optional: true, Description: "Enable redistribution of specific route types.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateDiagFunc: ValidationMultiValInSlice([]string{"bgp", "connected", "copy", "dhcp", "fantasy", "modem", "ospf", "rip", "static", "vpn"}, false, false), }, }, "router_id": { Type: schema.TypeString, Optional: true, Default: "main", Description: "OSPF Router ID. Can be set explicitly as an IP address, or as the name " + "of the router-id instance.", }, "routing_table": { Type: schema.TypeString, Optional: true, Description: "Name of the routing table in use.", }, "version": { Type: schema.TypeInt, Optional: true, Default: 2, Description: "OSPF version this instance will be running (v2 for IPv4, v3 for IPv6).", ValidateFunc: validation.IntBetween(2, 3), }, KeyVrf: PropVrfRw, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_routing_ospf_instance_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testRoutingOspfInstance = "routeros_routing_ospf_instance.test_routing_ospf_instance" func TestAccRoutingOspfInstanceTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/routing/ospf/instance", "routeros_routing_ospf_instance"), Steps: []resource.TestStep{ { Config: testAccCheckRoutingOspfInstanceConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testRoutingOspfInstance), resource.TestCheckResourceAttr(testRoutingOspfInstance, "name", "test_routing_ospf_instance"), ), }, }, }) }) } } func testAccCheckRoutingOspfInstanceConfig() string { return providerConfig + ` resource "routeros_routing_ospf_instance" "test_routing_ospf_instance" { name = "test_routing_ospf_instance" disabled = false } ` } ================================================ FILE: routeros/resource_routing_ospf_interface_template.go ================================================ package routeros import ( "context" "reflect" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*54", "interfaces": "", "network": "", "area": "", "auth": "", "auth-id": "", "authentication-key": "", "comment": "", "cost": "", "dead-interval": "", "disabled": "", "hello-interval": "", "instance-id": "", "passive": "", "prefix-list": "", "priority": "", "retransmit-interval": "", "transmit-delay": "", "type": "", "vlink-neighbor-id": "", "vlink-transit-area": "", } */ // ResourceRoutingOspfInterfaceTemplate https://help.mikrotik.com/docs/display/ROS/OSPF func ResourceRoutingOspfInterfaceTemplate() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/routing/ospf/interface-template"), MetaId: PropId(Id), MetaSetUnsetFields: PropSetUnsetFields("passive"), "area": { Type: schema.TypeString, Required: true, Description: "The OSPF area to which the matching interface will be associated.", }, "auth": { Type: schema.TypeString, Optional: true, Description: "Specifies authentication method for OSPF protocol messages.", ValidateFunc: validation.StringInSlice([]string{"simple", "md5", "sha1", "sha256", "sha384", "sha512"}, true), }, "auth_id": { Type: schema.TypeInt, Optional: true, Description: "The key id is used to calculate message digest (used when MD5 or SHA authentication is enabled).", ValidateFunc: validation.IntBetween(0, 255), }, "authentication_key": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "The authentication key to be used, should match on all the neighbors of the network segment " + "(for versions before RouterOS 7.x).", }, "auth_key": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "The authentication key to be used, should match on all the neighbors of the network segment " + "(available since RouterOS 7.x).", }, KeyComment: PropCommentRw, "cost": { Type: schema.TypeInt, Optional: true, Default: 1, Description: "Interface cost expressed as link state metric.", ValidateFunc: Validation64k, }, "dead_interval": { Type: schema.TypeString, Optional: true, Default: "40s", Description: "Specifies the interval after which a neighbor is declared dead.", ValidateFunc: ValidationTime, DiffSuppressFunc: TimeEqual, }, KeyDisabled: PropDisabledRw, "hello_interval": { Type: schema.TypeString, Optional: true, Default: "10s", Description: "The interval between HELLO packets that the router sends out this interface.", ValidateFunc: ValidationTime, DiffSuppressFunc: TimeEqual, }, KeyInactive: PropInactiveRo, "interfaces": { Type: schema.TypeSet, Optional: true, Description: "Interfaces to match.", Elem: &schema.Schema{ Type: schema.TypeString, }, }, "instance_id": { Type: schema.TypeInt, Optional: true, Description: "Interface cost expressed as link state metric.", Default: 0, ValidateFunc: validation.IntBetween(0, 255), }, "networks": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.IsCIDR, }, Description: "The network prefixes associated with the area.", }, "passive": { Type: schema.TypeBool, Optional: true, Default: false, Description: "If enabled, then do not send or receive OSPF traffic on the matching interfaces. " + "The correct value of this attribute may not be displayed in Winbox. " + "Please check the parameters in the console!", }, "prefix_list": { Type: schema.TypeString, Optional: true, Description: "Name of the address list containing networks that should be advertised to the v3 interface.", }, "priority": { Type: schema.TypeInt, Optional: true, Description: "Router's priority. Used to determine the designated router in a broadcast network.", Default: 128, ValidateFunc: validation.IntBetween(0, 255), }, "retransmit_interval": { Type: schema.TypeString, Optional: true, Default: "5s", Description: "Time interval the lost link state advertisement will be resent.", ValidateFunc: ValidationTime, DiffSuppressFunc: TimeEqual, }, "transmit_delay": { Type: schema.TypeString, Optional: true, Default: "1s", Description: "Link-state transmit delay is the estimated time it takes to transmit a link-state " + "update packet on the interface.", ValidateFunc: ValidationTime, DiffSuppressFunc: TimeEqual, }, "type": { Type: schema.TypeString, Description: "The OSPF network type on this interface.", Optional: true, Default: "broadcast", ValidateFunc: validation.StringInSlice( []string{"broadcast", "nbma", "ptp", "ptmp", "ptp-unnumbered", "virtual-link"}, true), }, "vlink_neighbor_id": { Type: schema.TypeString, Optional: true, Description: "Specifies the router-id of the neighbor which should be connected over the virtual link.", }, "vlink_transit_area": { Type: schema.TypeString, Optional: true, Description: "A non-backbone area the two routers have in common over which the virtual link will " + "be established.", }, "use_bfd": { Type: schema.TypeBool, Optional: true, Description: "Whether to use the BFD protocol for faster connection state detection.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, SchemaVersion: 1, StateUpgraders: []schema.StateUpgrader{ { Type: ResourceRoutingOspfInterfaceTemplateV0().CoreConfigSchema().ImpliedType(), Upgrade: func(ctx context.Context, rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) { defer delete(rawState, "network") if rawState["network"] == nil { return rawState, nil } value := reflect.ValueOf(rawState["network"]) if value.IsZero() { rawState["networks"] = []interface{}{} return rawState, nil } slice := reflect.MakeSlice(reflect.SliceOf(value.Type()), 0, 1) reflect.Append(slice, value) rawState["networks"] = slice.Interface() return rawState, nil }, Version: 0, }, }, } } ================================================ FILE: routeros/resource_routing_ospf_interface_template_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testRoutingOspfInterfaceTemplate = "routeros_routing_ospf_interface_template.test_routing_ospf_interface_template" func TestAccRoutingOspfInterfaceTemplateTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/routing/ospf/interface-template", "routeros_routing_ospf_interface_template"), Steps: []resource.TestStep{ { Config: testAccCheckRoutingOspfInterfaceTemplateConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testRoutingOspfInterfaceTemplate), resource.TestCheckResourceAttr(testRoutingOspfInterfaceTemplate, "area", "test_routing_ospf_area"), ), }, }, }) }) } } func testAccCheckRoutingOspfInterfaceTemplateConfig() string { return providerConfig + ` resource "routeros_routing_ospf_instance" "test_routing_ospf_instance" { name = "test_routing_ospf_instance" disabled = false } resource "routeros_routing_ospf_area" "test_routing_ospf_area" { name = "test_routing_ospf_area" disabled = false instance = routeros_routing_ospf_instance.test_routing_ospf_instance.name } resource "routeros_routing_ospf_interface_template" "test_routing_ospf_interface_template" { area = routeros_routing_ospf_area.test_routing_ospf_area.name interfaces = ["ether3"] passive = true } ` } ================================================ FILE: routeros/resource_routing_ospf_interface_template_v0.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*54", "interfaces": "", "network": "", "area": "", "auth": "", "auth-id": "", "authentication-key": "", "comment": "", "cost": "", "dead-interval": "", "disabled": "", "hello-interval": "", "instance-id": "", "passive": "", "prefix-list": "", "priority": "", "retransmit-interval": "", "transmit-delay": "", "type": "", "vlink-neighbor-id": "", "vlink-transit-area": "", } */ // ResourceRoutingOspfInterfaceTemplate https://help.mikrotik.com/docs/display/ROS/OSPF func ResourceRoutingOspfInterfaceTemplateV0() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/routing/ospf/interface-template"), MetaId: PropId(Id), MetaSetUnsetFields: PropSetUnsetFields("passive"), "area": { Type: schema.TypeString, Required: true, Description: "The OSPF area to which the matching interface will be associated.", }, "auth": { Type: schema.TypeString, Optional: true, Description: "Specifies authentication method for OSPF protocol messages.", ValidateFunc: validation.StringInSlice([]string{"simple", "md5", "sha1", "sha256", "sha384", "sha512"}, true), }, "auth_id": { Type: schema.TypeInt, Optional: true, Description: "The key id is used to calculate message digest (used when MD5 or SHA authentication is enabled).", ValidateFunc: validation.IntBetween(0, 255), }, "authentication_key": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "The authentication key to be used, should match on all the neighbors of the network segment " + "(for versions before RouterOS 7.x).", }, "auth_key": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "The authentication key to be used, should match on all the neighbors of the network segment " + "(available since RouterOS 7.x).", }, KeyComment: PropCommentRw, "cost": { Type: schema.TypeInt, Optional: true, Default: 1, Description: "Interface cost expressed as link state metric.", ValidateFunc: Validation64k, }, "dead_interval": { Type: schema.TypeString, Optional: true, Default: "40s", Description: "Specifies the interval after which a neighbor is declared dead.", ValidateFunc: ValidationTime, DiffSuppressFunc: TimeEqual, }, KeyDisabled: PropDisabledRw, "hello_interval": { Type: schema.TypeString, Optional: true, Default: "10s", Description: "The interval between HELLO packets that the router sends out this interface.", ValidateFunc: ValidationTime, DiffSuppressFunc: TimeEqual, }, KeyInactive: PropInactiveRo, "interfaces": { Type: schema.TypeSet, Optional: true, Description: "Interfaces to match.", Elem: &schema.Schema{ Type: schema.TypeString, }, }, "instance_id": { Type: schema.TypeInt, Optional: true, Description: "Interface cost expressed as link state metric.", Default: 0, ValidateFunc: validation.IntBetween(0, 255), }, "network": { Type: schema.TypeString, Optional: true, Description: "The network prefix associated with the area.", }, "passive": { Type: schema.TypeBool, Optional: true, Default: false, Description: "If enabled, then do not send or receive OSPF traffic on the matching interfaces. " + "The correct value of this attribute may not be displayed in Winbox. " + "Please check the parameters in the console!", }, "prefix_list": { Type: schema.TypeString, Optional: true, Description: "Name of the address list containing networks that should be advertised to the v3 interface.", }, "priority": { Type: schema.TypeInt, Optional: true, Description: "Router's priority. Used to determine the designated router in a broadcast network.", Default: 128, ValidateFunc: validation.IntBetween(0, 255), }, "retransmit_interval": { Type: schema.TypeString, Optional: true, Default: "5s", Description: "Time interval the lost link state advertisement will be resent.", ValidateFunc: ValidationTime, DiffSuppressFunc: TimeEqual, }, "transmit_delay": { Type: schema.TypeString, Optional: true, Default: "1s", Description: "Link-state transmit delay is the estimated time it takes to transmit a link-state " + "update packet on the interface.", ValidateFunc: ValidationTime, DiffSuppressFunc: TimeEqual, }, "type": { Type: schema.TypeString, Description: "The OSPF network type on this interface.", Optional: true, Default: "broadcast", ValidateFunc: validation.StringInSlice( []string{"broadcast", "nbma", "ptp", "ptmp", "ptp-unnumbered", "virtual-link"}, true), }, "vlink_neighbor_id": { Type: schema.TypeString, Optional: true, Description: "Specifies the router-id of the neighbor which should be connected over the virtual link.", }, "vlink_transit_area": { Type: schema.TypeString, Optional: true, Description: "A non-backbone area the two routers have in common over which the virtual link will " + "be established.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_routing_rule.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", ".nextid": "*FFFFFFFF", "action": "lookup", "disabled": "false", "dst-address": "2.2.2.0/24", "inactive": "false", "interface": "bridge1", "routing-mark": "main", "src-address": "1.1.1.1/32", "table": "main" } */ // https://help.mikrotik.com/docs/display/ROS/Policy+Routing func ResourceRoutingRule() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/routing/rule"), MetaId: PropId(Id), "action": { Type: schema.TypeString, Optional: true, Description: "An action to take on the matching packet:\n * drop - silently drop the packet.\n * lookup - perform a " + "lookup in routing tables.\n * lookup-only-in-table - perform lookup only in the specified routing table " + "(see table parameter).\n * unreachable - generate ICMP unreachable message and send it back to the source.", ValidateFunc: validation.StringInSlice([]string{"drop", "lookup", "lookup-only-in-table", "unreachable"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyComment: PropCommentRw, "dst_address": { Type: schema.TypeString, Optional: true, Description: "The destination address of the packet to match.", }, KeyDisabled: PropDisabledRw, KeyInactive: PropInactiveRo, "interface": { Type: schema.TypeString, Optional: true, Description: "Incoming interface to match.", }, "min_prefix": { Type: schema.TypeInt, Optional: true, Description: "Equivalent to Linux IP rule `suppress_prefixlength`. For example to suppress the default route " + "in the routing decision set the value to 0.", }, "routing_mark": { Type: schema.TypeString, Optional: true, Description: "Match specific routing mark.", }, "src_address": { Type: schema.TypeString, Optional: true, Description: "The source address of the packet to match.", }, "table": { Type: schema.TypeString, Optional: true, Description: "Name of the routing table to use for lookup.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_routing_rule_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testRoutingRule = "routeros_routing_rule.test" func TestAccRoutingRuleTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccRoutingRuleConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testRoutingRule), resource.TestCheckResourceAttr(testRoutingRule, "dst_address", "192.168.1.0/24"), resource.TestCheckResourceAttr(testRoutingRule, "action", "lookup-only-in-table"), resource.TestCheckResourceAttr(testRoutingRule, "interface", "ether1"), ), }, { Config: testAccRoutingRuleConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testRoutingRule), resource.TestCheckResourceAttr(testRoutingRule, "dst_address", "192.168.1.0/24"), resource.TestCheckResourceAttr(testRoutingRule, "action", "lookup-only-in-table"), resource.TestCheckResourceAttr(testRoutingRule, "interface", "ether1"), ), }, }, }) }) } } func testAccRoutingRuleConfig() string { return fmt.Sprintf(`%v resource "routeros_routing_rule" "test" { dst_address = "192.168.1.0/24" action = "lookup-only-in-table" interface = "ether1" } `, providerConfig) } ================================================ FILE: routeros/resource_routing_table.go ================================================ package routeros import ( "context" "fmt" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*0", "dynamic": "true", "fib": "", "invalid": "false", "name": "main" }, */ // https://help.mikrotik.com/docs/display/ROS/Policy+Routing func ResourceRoutingTable() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/routing/table"), MetaId: PropId(Id), KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, KeyDynamic: PropDynamicRo, "fib": { Type: schema.TypeBool, Optional: true, Default: true, ForceNew: true, Description: "fib parameter should be specified if the routing table is intended to push routes to the FIB.", }, KeyInvalid: PropInvalidRo, "name": { Type: schema.TypeString, Optional: true, Description: "Routing table name.", }, } return &schema.Resource{ ReadContext: func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { metadata := GetMetadata(resSchema) res, err := ReadItems(&ItemId{metadata.IdType, d.Id()}, metadata.Path, m.(Client)) if err != nil { ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgGet, err)) return diag.FromErr(err) } // Resource not found. if len(*res) == 0 { d.SetId("") return nil } r := (*res)[0] d.SetId(r.GetID(metadata.IdType)) // These are crutches, but I don't know an easier way to implement this strange logic. if _, ok := r["fib"]; ok { r["fib"] = "yes" } else { r["fib"] = "no" } return MikrotikResourceDataToTerraform(r, resSchema, d) }, CreateContext: func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { item, metadata := TerraformResourceDataToMikrotik(resSchema, d) if b, ok := item["fib"]; ok { if b == "no" { delete(item, "fib") } else { item["fib"] = "" } } res, err := CreateItem(ctx, item, metadata.Path, m.(Client)) if err != nil { ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgPut, err)) return diag.FromErr(err) } // ... If no ID is set, Terraform assumes the resource was not created successfully; // as a result, no state will be saved for that resource. if res.GetID(Id) == "" { return diag.Diagnostics{ diag.Diagnostic{ Severity: diag.Error, Summary: "The resource ID was not found in the response", }, } } // Response ID. d.SetId(res.GetID(Id)) // We ask for information again in the case of API. if m.(Client).GetTransport() == TransportAPI { r, err := ReadItems(&ItemId{Id, res.GetID(Id)}, metadata.Path, m.(Client)) if err != nil { ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgPut, err)) return diag.FromErr(err) } if len(*r) == 0 { return diag.Diagnostics{ diag.Diagnostic{ Severity: diag.Error, Summary: fmt.Sprintf("Mikrotik resource path='%v' id='%v' not found", metadata.Path, res.GetID(Id)), }, } } res = (*r)[0] if _, ok := res["fib"]; ok { res["fib"] = "yes" } else { res["fib"] = "no" } } return MikrotikResourceDataToTerraform(res, resSchema, d) }, UpdateContext: func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { item, metadata := TerraformResourceDataToMikrotik(resSchema, d) if b, ok := item["fib"]; ok { if b == "no" { delete(item, "fib") } else { item["fib"] = "" } } id, err := dynamicIdLookup(metadata.IdType, metadata.Path, m.(Client), d) if err != nil { // There is nothing to update, because resource id not found // or some other error. ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgPatch, err)) return diag.FromErr(err) } res, err := UpdateItem(&ItemId{Id, id}, metadata.Path, item, m.(Client)) if err != nil { ColorizedDebug(ctx, fmt.Sprintf(ErrorMsgPatch, err)) return diag.FromErr(err) } if _, ok := res["fib"]; ok { res["fib"] = "yes" } else { res["fib"] = "no" } return MikrotikResourceDataToTerraform(res, resSchema, d) }, DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_routing_table_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testRoutingTableAddress = "routeros_routing_table.test_table" func TestAccRoutingTableTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/routing/table", "routeros_routing_table"), Steps: []resource.TestStep{ { Config: testAccRoutingTableConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testRoutingTableAddress), resource.TestCheckResourceAttr(testRoutingTableAddress, "name", "to_ISP1"), ), }, }, }) }) } } func testAccRoutingTableConfig() string { return providerConfig + ` resource "routeros_routing_table" "test_table" { name = "to_ISP1" fib = false } ` } ================================================ FILE: routeros/resource_snmp.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { "contact": "", "enabled": "false", "engine-id": "80003a8c04", "engine-id-suffix": "", "location": "", "src-address": "::", "trap-community": "public", "trap-generators": "temp-exception", "trap-target": "", "trap-version": "1", "vrf": "main" } */ // https://help.mikrotik.com/docs/display/ROS/SNMP func ResourceSNMP() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/snmp"), MetaId: PropId(Id), "contact": { Type: schema.TypeString, Optional: true, Description: "Contact information.", }, KeyEnabled: PropEnabled("Used to disable/enable SNMP service"), "engine_id": { Type: schema.TypeString, Computed: true, Description: "For SNMP v3, used as part of identifier. You can configure suffix part of engine id " + "using this argument. If SNMP client is not capable to detect set engine-id value then " + "this prefix hex have to be used 0x80003a8c04", }, "engine_id_suffix": { Type: schema.TypeString, Optional: true, Description: "Unique identifier for an SNMPv3 engine by configuring the suffix of the engine ID.", }, "location": { Type: schema.TypeString, Optional: true, Description: "Location information.", }, "trap_community": { Type: schema.TypeString, Optional: true, Computed: true, Sensitive: true, Description: "Which communities configured in community menu to use when sending out the trap. " + "This name must be present in the community list.", }, "trap_generators": { Type: schema.TypeString, Optional: true, Computed: true, Description: "What action will generate traps:\n * interfaces - interface changes;\n * start-trap - snmp " + "server starting on the router.", ValidateFunc: validation.StringInSlice([]string{"interfaces", "start-trap", "temp-exception"}, false), }, "trap_interfaces": { Type: schema.TypeString, Optional: true, Description: "List of interfaces that traps are going to be sent out.", }, "trap_target": { Type: schema.TypeSet, Optional: true, Description: "IP (IPv4 or IPv6) addresses of SNMP data collectors that have to receive the trap.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.IsIPAddress, }, }, "trap_version": { Type: schema.TypeInt, Optional: true, Computed: true, Description: "Version of SNMP protocol to use for trap.", ValidateFunc: validation.IntBetween(1, 3), }, "src_address": { Type: schema.TypeString, Optional: true, Description: "Force the router to always use the same IP source address for all of the SNMP messages.", ValidateFunc: validation.IsIPAddress, DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { if old == new { return true } if (old == "" && new == "::") || (old == "::" && new == "") { return true } if old == "" || new == "" { return false } return false }, }, KeyVrf: PropVrfRw, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_snmp_community.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*2", "addresses": "::/0", "authentication-password": "", "authentication-protocol": "MD5", "comment": "Comment", "default": "false", "disabled": "true", "encryption-password": "", "encryption-protocol": "DES", "name": "private", "read-access": "true", "security": "none", "write-access": "false" } */ // https://help.mikrotik.com/docs/display/ROS/SNMP#SNMP-CommunityProperties func ResourceSNMPCommunity() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/snmp/community"), MetaId: PropId(Id), "addresses": { Type: schema.TypeSet, Optional: true, Description: "Set of IP (v4 or v6) addresses or CIDR networks from which connections to SNMP server are allowed.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.Any( validation.IsIPv4Address, validation.IsIPv6Address, validation.IsCIDRNetwork(0, 128), ), }, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "authentication_password": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "Password used to authenticate the connection to the server (SNMPv3).", }, "authentication_protocol": { Type: schema.TypeString, Optional: true, Default: "MD5", Description: "The protocol used for authentication (SNMPv3).", ValidateFunc: validation.StringInSlice([]string{"MD5", "SHA1"}, false), }, KeyComment: PropCommentRw, KeyDefault: PropDefaultRo, KeyDisabled: PropDisabledRw, "encryption_password": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "The password used for encryption (SNMPv3).", }, "encryption_protocol": { Type: schema.TypeString, Optional: true, Default: "DES", Description: "encryption protocol to be used to encrypt the communication (SNMPv3). AES (see rfc3826) " + "available since v6.16.", ValidateFunc: validation.StringInSlice([]string{"DES", "AES"}, false), }, "name": { Type: schema.TypeString, Optional: true, Description: "Community Name.", }, "read_access": { Type: schema.TypeBool, Optional: true, Default: true, Description: "Whether read access is enabled for this community.", }, "security": { Type: schema.TypeString, Optional: true, Default: "none", Description: "Security features.", ValidateFunc: validation.StringInSlice([]string{"authorized", "none", "private"}, false), }, "write_access": { Type: schema.TypeBool, Optional: true, Description: "Whether write access is enabled for this community.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, SchemaVersion: 1, StateUpgraders: []schema.StateUpgrader{ { Type: ResourceSNMPCommunityV0().CoreConfigSchema().ImpliedType(), Upgrade: stateMigrationScalarToList("addresses"), Version: 0, }, }, } } ================================================ FILE: routeros/resource_snmp_community_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testSNMPCommunityAddress = "routeros_snmp_community.test" func TestAccSNMPCommunityTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/snmp/community", "routeros_snmp_community"), Steps: []resource.TestStep{ { Config: testAccSNMPCommunityConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testSNMPCommunityAddress), resource.TestCheckResourceAttr(testSNMPCommunityAddress, "name", "private"), ), }, }, }) }) } } func testAccSNMPCommunityConfig() string { return providerConfig + ` resource "routeros_snmp_community" "test" { authentication_password = "authpasswd" authentication_protocol = "MD5" comment = "Comment" disabled = true encryption_password = "encpassword" encryption_protocol = "DES" name = "private" read_access = true security = "private" write_access = true }` } ================================================ FILE: routeros/resource_snmp_community_v0.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*2", "addresses": "::/0", "authentication-password": "", "authentication-protocol": "MD5", "comment": "Comment", "default": "false", "disabled": "true", "encryption-password": "", "encryption-protocol": "DES", "name": "private", "read-access": "true", "security": "none", "write-access": "false" } */ // https://help.mikrotik.com/docs/display/ROS/SNMP#SNMP-CommunityProperties func ResourceSNMPCommunityV0() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/snmp/community"), MetaId: PropId(Id), "addresses": { Type: schema.TypeString, Optional: true, Description: "Set of IP (v4 or v6) addresses or CIDR networks from which connections to SNMP server are allowed.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "authentication_password": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "Password used to authenticate the connection to the server (SNMPv3).", }, "authentication_protocol": { Type: schema.TypeString, Optional: true, Default: "MD5", Description: "The protocol used for authentication (SNMPv3).", ValidateFunc: validation.StringInSlice([]string{"MD5", "SHA1"}, false), }, KeyComment: PropCommentRw, KeyDefault: PropDefaultRo, KeyDisabled: PropDisabledRw, "encryption_password": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "The password used for encryption (SNMPv3).", }, "encryption_protocol": { Type: schema.TypeString, Optional: true, Default: "DES", Description: "encryption protocol to be used to encrypt the communication (SNMPv3). AES (see rfc3826) " + "available since v6.16.", ValidateFunc: validation.StringInSlice([]string{"DES", "AES"}, false), }, "name": { Type: schema.TypeString, Optional: true, Description: "Community Name.", }, "read_access": { Type: schema.TypeBool, Optional: true, Default: true, Description: "Whether read access is enabled for this community.", }, "security": { Type: schema.TypeString, Optional: true, Default: "none", Description: "Security features.", ValidateFunc: validation.StringInSlice([]string{"authorized", "none", "private"}, false), }, "write_access": { Type: schema.TypeBool, Optional: true, Description: "Whether write access is enabled for this community.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_snmp_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" ) const testSNMPMinVersion = "7.8" const testSNMPAddress = "routeros_snmp.test" func TestAccSNMPTest_basic(t *testing.T) { if !testCheckMinVersion(t, testSNMPMinVersion) { t.Logf("Test skipped, the minimum required version is %v", testSNMPMinVersion) return } for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccSNMPConfig(0), Check: resource.ComposeTestCheckFunc( testAccCheckSNMPExists(testSNMPAddress), resource.TestCheckResourceAttr(testSNMPAddress, "enabled", "true"), ), }, { Config: testAccSNMPConfig(1), Check: resource.ComposeTestCheckFunc( testAccCheckSNMPExists(testSNMPAddress), resource.TestCheckResourceAttr(testSNMPAddress, "enabled", "false"), ), }, }, }) }) } } func testAccCheckSNMPExists(name string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[name] if !ok { return fmt.Errorf("not found: %s", name) } if rs.Primary.ID == "" { return fmt.Errorf("no id is set") } return nil } } func testAccSNMPConfig(n int) string { var conf = []string{ ` resource "routeros_snmp_community" "test" { name = "private" } resource "routeros_snmp" "test" { contact = "John D." enabled = true engine_id_suffix = "8a3c" location = "Backyard" trap_community = "private" trap_generators = "start-trap" trap_version = 3 depends_on = [routeros_snmp_community.test] }`, ` resource "routeros_snmp" "test" { contact = "" enabled = false engine_id_suffix = "" location = "" trap_community = "public" trap_generators = "temp-exception" trap_version = 1 }`, } return providerConfig + conf[n] } ================================================ FILE: routeros/resource_system_certificate.go ================================================ package routeros import ( "context" "fmt" "github.com/hashicorp/go-uuid" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "akid": "", "authority": "true", "common-name": "MyRouter", "crl": "false", "days-valid": "3650", "digest-algorithm": "sha256", "expires-after": "519w4d15h28m25s", "fingerprint": "ad9b324e93dee5135d2f6292480b78a5e00ae7ab44bf082b10cb1947993793c7", "invalid-after": "mar/10/2033 17:57:09", "invalid-before": "mar/13/2023 17:57:09", "key-size": "2048", "key-type": "rsa", "key-usage": "key-cert-sign,crl-sign", "name": "root-cert", "private-key": "true", "serial-number": "21B9F571B54195D3", "skid": "c90ec1a6d381b97bfa6b2c2c5c3ee81cf80ea729", "smart-card-key": "false", "subject-alt-name": "", "trusted": "true" } */ // https://help.mikrotik.com/docs/display/ROS/Certificates // https://wiki.mikrotik.com/wiki/Manual:System/Certificates func ResourceSystemCertificate() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/certificate"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("import", "sign", "sign_via_scep", "cert_file_content", "key_file_content"), "authority": { Type: schema.TypeString, Computed: true, }, "akid": { Type: schema.TypeString, Computed: true, Description: "Authority Key Identifier.", }, "ca": { Type: schema.TypeString, Computed: true, }, "ca_crl_host": { Type: schema.TypeString, Computed: true, }, "ca_fingerprint": { Type: schema.TypeString, Computed: true, }, "challenge_password": { Type: schema.TypeString, Computed: true, Sensitive: true, Description: "A challenge password for scep client.", }, "common_name": { Type: schema.TypeString, Required: true, ForceNew: true, Description: "Common Name (e.g. server FQDN or YOUR name).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "copy_from": { Type: schema.TypeString, Optional: true, ForceNew: true, Description: "", }, "country": { Type: schema.TypeString, Optional: true, ForceNew: true, Description: "Country Name (2 letter code).", ValidateFunc: validation.StringLenBetween(2, 2), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "crl": { Type: schema.TypeString, Computed: true, }, "days_valid": { Type: schema.TypeInt, Optional: true, ForceNew: true, Description: "Certificate lifetime.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "dsa": { Type: schema.TypeBool, Computed: true, }, "digest_algorithm": { Type: schema.TypeBool, Computed: true, }, "expired": { Type: schema.TypeBool, Computed: true, Description: "Set to true if certificate is expired.", }, "expires_after": { Type: schema.TypeString, Computed: true, }, "fingerprint": { Type: schema.TypeString, Computed: true, }, "import": { Type: schema.TypeSet, Optional: true, ForceNew: true, ConflictsWith: []string{"sign", "sign_via_scep"}, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "cert_file_content": { Type: schema.TypeString, Optional: true, ForceNew: true, Description: "Certificate in PEM format.", }, "cert_file_name": { Type: schema.TypeString, Optional: true, ForceNew: true, Description: "Certificate file name that will be imported.", }, "key_file_content": { Type: schema.TypeString, Optional: true, ForceNew: true, Description: "Key in PEM format.", }, "key_file_name": { Type: schema.TypeString, Optional: true, ForceNew: true, Description: "Key file name that will be imported.", }, "passphrase": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "File passphrase if there is such.", }, }, }, }, "invalid_after": { Type: schema.TypeString, Computed: true, Description: "The date after which certificate wil be invalid.", }, "invalid_before": { Type: schema.TypeString, Computed: true, Description: "The date before which certificate is invalid.", }, "issued": { Type: schema.TypeString, Computed: true, }, "issuer": { Type: schema.TypeString, Computed: true, }, "key_type": { Type: schema.TypeString, Computed: true, }, "key_size": { Type: schema.TypeString, Optional: true, Computed: true, ForceNew: true, ValidateFunc: validation.StringInSlice([]string{"1024", "1536", "2048", "4096", "8192", "prime256v1", "secp384r1", "secp521r1"}, false), }, "key_usage": { Type: schema.TypeSet, Optional: true, ForceNew: true, Description: "Detailed key usage descriptions can be found in RFC 5280.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice( []string{ "digital-signature", "content-commitment", "key-encipherment", "data-encipherment", "key-agreement", "key-cert-sign", "crl-sign", "encipher-only", "decipher-only", "dvcs", "server-gated-crypto", "ocsp-sign", "timestamp", "ipsec-user", "ipsec-tunnel", "ipsec-end-system", "email-protect", "code-sign", "tls-server", "tls-client", }, false)}, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "locality": { Type: schema.TypeString, Optional: true, ForceNew: true, Description: "Locality Name (eg, city).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyName: PropName("Name of the certificate. Name can be edited."), "organization": { Type: schema.TypeString, Optional: true, ForceNew: true, Description: "Organizational Unit Name (eg, section)", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "private_key": { Type: schema.TypeBool, Computed: true, }, "req_fingerprint": { Type: schema.TypeString, Computed: true, }, "revoked": { Type: schema.TypeString, Computed: true, }, "scep_url": { Type: schema.TypeString, Computed: true, }, "serial_number": { Type: schema.TypeString, Computed: true, }, "sign": { Type: schema.TypeSet, Optional: true, ForceNew: true, ConflictsWith: []string{"sign_via_scep"}, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "ca": { Type: schema.TypeString, Optional: true, ForceNew: true, Description: "Which CA to use if signing issued certificates.", }, "ca_crl_host": { Type: schema.TypeString, Optional: true, ForceNew: true, Description: "CRL host if issuing CA certificate.", }, // "ca_on_smart_card": { // Type: schema.TypeString, // Optional: true, // Description: "", // }, // We do not change the name of the certificate after signing so that there is non empty plan. // "name": { // Type: schema.TypeString, // Optional: true, // Description: "What name to assign to issued certificate.", // }, }, }, }, "sign_via_scep": { Type: schema.TypeSet, Optional: true, ForceNew: true, ConflictsWith: []string{"sign"}, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "scep_url": { Type: schema.TypeString, Required: true, ForceNew: true, ValidateFunc: validation.IsURLWithScheme([]string{"http"}), Description: "HTTP URL to the SCEP server.", }, "challenge_password": { Type: schema.TypeString, Optional: true, Sensitive: true, ForceNew: true, Description: "A challenge password.", }, "ca_identity": { Type: schema.TypeString, Optional: true, ForceNew: true, Description: "SCEP CA identity.", }, "on_smart_card": { Type: schema.TypeBool, Optional: true, ForceNew: true, Description: "Whether to store a private key on smart card if hardware supports it.", }, "refresh": { Type: schema.TypeBool, Optional: true, Default: true, ForceNew: true, Description: "Check certificate expiration and refresh it if expired.", }, }, }, }, "skid": { Type: schema.TypeString, Computed: true, Description: "Subject Key Identifier.", }, "smart_card_key": { Type: schema.TypeString, Computed: true, }, "state": { Type: schema.TypeString, Optional: true, ForceNew: true, Description: "State or Province Name (full name).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "status": { Type: schema.TypeString, Computed: true, Description: "Shows current status of scep client.", }, "subject_alt_name": { Type: schema.TypeString, Optional: true, ForceNew: true, Description: "SANs (subject alternative names).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "trusted": { Type: schema.TypeBool, Optional: true, Description: "If set to yes certificate is included 'in trusted certificate chain'.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "unit": { Type: schema.TypeString, Optional: true, ForceNew: true, Description: "Organizational Unit Name (eg, section).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } certImport := func(ctx context.Context, attrBlock any, d *schema.ResourceData, m interface{}) diag.Diagnostics { var certName string bl := attrBlock.(*schema.Set).List()[0].(map[string]interface{}) var resUrl = &URL{Path: resSchema[MetaResourcePath].Default.(string)} if m.(Client).GetTransport() == TransportREST { resUrl.Path += "/import" } if data := bl["cert_file_content"].(string); data != "" { // Validation if bl["cert_file_name"].(string) != "" { return diag.Errorf("%q: conflicts with %s", "cert_file_content", "cert_file_name") } name, err := uuid.GenerateUUID() if err != nil { return diag.FromErr(err) } certFileId, diags := fileCreate(ctx, name, data, m) if diags != nil { return diags } ColorizedDebug(ctx, fmt.Sprintf("The certificate has been placed on file '%v'", name)) bl["cert_file_name"] = name defer func() { diags = fileDelete(ctx, certFileId, m) if diags != nil { ColorizedMessage(ctx, ERROR, "Certificate file deletion error", map[string]interface{}{"diags": diags}) } }() } if data := bl["key_file_content"].(string); data != "" { // Validation if bl["key_file_name"].(string) != "" { return diag.Errorf("%q: conflicts with %s", "key_file_content", "key_file_name") } name, err := uuid.GenerateUUID() if err != nil { return diag.FromErr(err) } bl["key_file_name"] = name // File is deleted by the MT after import. // https://github.com/terraform-routeros/terraform-provider-routeros/issues/660 _, diags := fileCreate(ctx, name, data, m) if diags != nil { return diags } ColorizedDebug(ctx, fmt.Sprintf("The private key has been placed on file '%v'", name)) } params := MikrotikItem{KeyName: d.Get(KeyName).(string), "file-name": bl["cert_file_name"].(string)} if passwd, ok := bl["passphrase"]; ok { params["passphrase"] = passwd.(string) } // Import certificate err := m.(Client).SendRequest(crudImport, resUrl, params, nil) if err != nil { return diag.FromErr(err) } if keyFile, ok := bl["key_file_name"]; ok && keyFile.(string) != "" { params = MikrotikItem{KeyName: d.Get(KeyName).(string), "file-name": keyFile.(string)} if passwd, ok := bl["passphrase"]; ok { params["passphrase"] = passwd.(string) } // Import key err := m.(Client).SendRequest(crudImport, resUrl, params, nil) if err != nil { return diag.FromErr(err) } } res, err := ReadItemsFiltered([]string{"name=" + d.Get(KeyName).(string)}, resSchema[MetaResourcePath].Default.(string), m.(Client)) if err != nil { return diag.FromErr(err) } switch len(*res) { case 0: return diag.Errorf("resource not found: name=%v", certName) case 1: retId, ok := (*res)[0][Id.String()] if !ok { return diag.Errorf("attribute %v not found in the response", Id.String()) } d.SetId(retId) default: return diag.Errorf("more than one resource found: name=%v", certName) } return ResourceRead(ctx, resSchema, d, m) } resCreate := func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { var diags diag.Diagnostics var cmdBlock any // User config for certificate signing var crudMethod crudMethod var command string // MikroTik command to sign certificate var ok bool if _, ok = d.GetOk("import"); !ok { // Run DefaultCreate. diags = ResourceCreate(ctx, resSchema, d, m) if diags.HasError() { return diags } } var params MikrotikItem // Parameters for MikroTik command if cmdBlock, ok = d.GetOk("sign"); ok { // {"number":"*54", ca: "Test-CA"} params = MikrotikItem{"number": d.Id()} crudMethod = crudSign // https://router/rest/certificate/sign command = "/sign" } else if cmdBlock, ok = d.GetOk("sign_via_scep"); ok { params = MikrotikItem{"template": d.Get("name").(string)} crudMethod = crudSignViaScep // https://router/rest/certificate/add-scep command = "/add-scep" } else if cmdBlock, ok = d.GetOk("import"); ok { return certImport(ctx, cmdBlock, d, m) } else { return diags } // []interface{map[string]interface{...}} for k, v := range cmdBlock.(*schema.Set).List()[0].(map[string]interface{}) { k = SnakeToKebab(k) switch v := v.(type) { case string: if v == "" { continue } params[k] = v case bool: params[k] = BoolToMikrotikJSON(v) default: panic("ResourceSystemCertificate resCreate: unhandled type switch") } } var resUrl = &URL{ Path: resSchema[MetaResourcePath].Default.(string), } if m.(Client).GetTransport() == TransportREST { resUrl.Path += command } err := m.(Client).SendRequest(crudMethod, resUrl, params, nil) if err != nil { return diag.FromErr(err) } return ResourceRead(ctx, resSchema, d, m) } resDelete := func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { // {"number":"*54"} item := MikrotikItem{"numbers": d.Id()} var resUrl = &URL{ Path: resSchema[MetaResourcePath].Default.(string), } var method crudMethod = crudRemove if _, ok := d.State().Attributes["ca"]; ok { // Not Root CA. method = crudRevoke } if m.(Client).GetTransport() == TransportREST { if _, ok := d.State().Attributes["ca"]; ok { // Not Root CA. resUrl.Path += "/issued-revoke" } else { // Root CA. resUrl.Path += "/remove" } } err := m.(Client).SendRequest(method, resUrl, item, nil) if err != nil { return diag.FromErr(err) } d.SetId("") return nil } return &schema.Resource{ CreateContext: resCreate, ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: resDelete, Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_system_certificate_import_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testSystemCertificatesImportAddress = "routeros_system_certificate.external" func TestAccSystemCertificatesTest_import(t *testing.T) { // ROS 7.12.x does not return IDs for created files. if !testCheckMinVersion(t, testFileMinVersion) { t.Logf("Test skipped, the minimum required version is %v", testFileMinVersion) return } for _, name := range testNames { t.Run(name, func(t *testing.T) { var externalCrt MikrotikItem resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/certificate", "routeros_system_certificate"), Steps: []resource.TestStep{ { Config: testAccSystemCertificatesImportConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testSystemCertificatesImportAddress), // external_crt testCheckResourceExists("routeros_system_certificate.external", "/certificate", &externalCrt), testCheckMikrotikItemAttr("routeros_system_certificate.external", &externalCrt, "name", "external.crt"), resource.TestCheckResourceAttr("routeros_system_certificate.external", "name", "external.crt"), resource.TestCheckResourceAttr("routeros_system_certificate.external", "common_name", "External Certificate"), resource.TestCheckResourceAttr("routeros_system_certificate.external", "private_key", "true"), ), }, }, }) }) } } func testAccSystemCertificatesImportConfig() string { return providerConfig + ` data "routeros_x509" "cert" { data = < 0 && oldValue[0] == '!' && newValue == "" { return true } if strings.HasSuffix(k, ".#") || d.GetRawConfig().GetAttr("policy").IsNull() { return true } return false }, }, "skin": { Type: schema.TypeString, Optional: true, Default: "default", Description: "The name of the skin that will be used for WebFig.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_system_user_group_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testUserGroupAddress = "routeros_system_user_group.test" func TestAccUserGroupTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/user/group", "routeros_system_user_group"), Steps: []resource.TestStep{ { Config: testAccUserGroupConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testUserGroupAddress), resource.TestCheckResourceAttr(testUserGroupAddress, "name", "terraform"), ), }, }, }) }) } } func testAccUserGroupConfig() string { return providerConfig + ` resource "routeros_system_user_group" "test" { name = "terraform" policy = ["api", "!ftp", "!local", "password", "policy", "read", "!reboot", "!rest-api", "!romon", "sensitive", "!sniff", "!ssh", "!telnet", "!test", "!web", "!winbox", "write"] } ` } ================================================ FILE: routeros/resource_system_user_settings.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { "minimum-categories": "0", "minimum-password-length": "0" } */ // https://help.mikrotik.com/docs/display/ROS/User#User-UserSettings func ResourceSystemUserSettings() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/user/settings"), MetaId: PropId(Id), "minimum_categories": { Type: schema.TypeInt, Optional: true, Default: 0, Description: "An option specifies the complexity requirements of the password, with categories being uppercase, lowercase, digit, and symbol.", ValidateFunc: validation.IntBetween(0, 4), }, "minimum_password_length": { Type: schema.TypeInt, Optional: true, Default: 0, Description: "An option specifies the minimum length of the password.", ValidateFunc: validation.IntAtLeast(0), }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_system_user_settings_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testUserSettings = "routeros_system_user_settings.test" func TestAccUserSettingsTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccUserSettingsConfig("1", "5"), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testUserSettings), resource.TestCheckResourceAttr(testUserSettings, "minimum_categories", "1"), resource.TestCheckResourceAttr(testUserSettings, "minimum_password_length", "5"), ), }, { Config: testAccUserSettingsConfig("0", "0"), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testUserSettings), resource.TestCheckResourceAttr(testUserSettings, "minimum_categories", "0"), resource.TestCheckResourceAttr(testUserSettings, "minimum_password_length", "0"), ), }, }, }) }) } } func testAccUserSettingsConfig(p1, p2 string) string { return fmt.Sprintf(`%v resource "routeros_system_user_settings" "test" { minimum_categories = %v minimum_password_length = %v } `, providerConfig, p1, p2) } ================================================ FILE: routeros/resource_system_user_sshkeys.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*1", "RSA":"true", "bits":"2048", "fingerprint": "SHA256:ABCD..." "key-owner":"admin", "key-type":"rsa", "user":"admin" } */ // https://help.mikrotik.com/docs/display/ROS/User#User-SSHKeys func ResourceUserSshKeys() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/user/ssh-keys"), MetaId: PropId(Id), MetaTransformSet: PropTransformSet("rsa: RSA"), "user": { Type: schema.TypeString, Required: true, Description: "username to which the SSH key is assigned.", }, KeyComment: PropCommentRw, "key": { Type: schema.TypeString, Required: true, ForceNew: true, Sensitive: true, Description: "key", }, "rsa": { Type: schema.TypeBool, Computed: true, Description: "key type is rsa", }, "bits": { Type: schema.TypeInt, Computed: true, Description: "key length", }, "key_type": { Type: schema.TypeString, Computed: true, Description: "key type", }, "key_owner": { Type: schema.TypeString, Computed: true, Description: "SSH key owner", }, "fingerprint": { Type: schema.TypeString, Computed: true, Description: "SSH key fingerprint", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_system_user_sshkeys_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testUserAddress2 = "routeros_system_user_sshkeys.test" func TestUserSshKeyTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/user/ssh-keys", "routeros_system_user_sshkeys"), Steps: []resource.TestStep{ { Config: testUserSshKeyConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testUserAddress2), resource.TestCheckResourceAttr(testUserAddress2, "key_type", "rsa"), ), }, }, }) }) } } func testUserSshKeyConfig() string { return providerConfig + ` resource "routeros_system_user" "test" { name = "test-user-1" address = "0.0.0.0/0" group = "read" password = "secret" comment = "Test User" } resource "routeros_system_user_sshkeys" "test" { depends_on = [routeros_system_user.test] user = "test-user-1" key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCyJ1EvW98veNVzR3VamNgmu0xOd/JK9YNvP/pa4WC5eT90UbX4TN7dKEK/x2FCwnnG9u0FQhzG2qa/Cg8meUvlfydn6uxc0/WCeXTKSu6sT63noPO6m4fHY7gu3Zt+fOc/WYGch9sBeWjZlCS1mA2lajkWhM3J8TFWCFm2Zk4/S3s5mt6VLbwpQnH2LhE41+azzDEVhcR6i3FfdgOF/J+j2fYYHJsBEKoQA5zUac2zWmz7X4Rv/g11ZBRqdMpHSD58o5F9lBb13antu5GcEs5RXpXp08OyXuRV9qhFpDBC8DOMALSOgT3vnu8uJLgo8QIulERofj/cRXbLCsmvMbpioBuGFXWx3ha4Ntd6z07kUh2KVbaIQLd/629UHNvgIhoBLlREJ8E5vllsX+jh8hRITHcCiEwXcDO+gG3hvJt0+jm8S8SObE/IHk8VuwWdhIsSku5vd+wVlxm8VeJzjc0cjdIiytvsq8VpLudKEUiqR0f2tHcoq8H+xcJv3Ycu1i8=" comment = "Test User" } ` } ================================================ FILE: routeros/resource_system_user_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testUserAddress = "routeros_system_user.test" func TestAccUserTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/user", "routeros_system_user"), Steps: []resource.TestStep{ { Config: testAccUserConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testUserAddress), resource.TestCheckResourceAttr(testUserAddress, "name", "test-user-1"), ), }, }, }) }) } } func testAccUserConfig() string { return providerConfig + ` resource "routeros_system_user" "test" { name = "test-user-1" address = "0.0.0.0/0" group = "read" password = "secret" comment = "Test User" } ` } ================================================ FILE: routeros/resource_testing_helper.go ================================================ package routeros import ( "fmt" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" ) func testResourcePrimaryInstanceId(name string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[name] if !ok { return fmt.Errorf("not found: %s", name) } if rs.Primary.ID == "" { return fmt.Errorf("no id is set") } return nil } } // testCheckMikrotikItemAttr ensures that an attribute in MikrotikItem parameters // with the given key equals a specific value func testCheckMikrotikItemAttr(name string, params *MikrotikItem, key string, value string) resource.TestCheckFunc { return func(s *terraform.State) error { if params == nil { return fmt.Errorf("%s: mikrotik params is nil", name) } v, ok := (*params)[key] if !ok { return fmt.Errorf("%s: attribute '%s' not found", name, key) } if v != value { return fmt.Errorf("%s: attribute '%s' expected %s, got %s", name, key, value, v) } return nil } } // testCheckMikrotikItemAttrSet ensures any value exists in MikrotikItem parameters // for the given key func testCheckMikrotikItemAttrSet(name string, params *MikrotikItem, key string) resource.TestCheckFunc { return func(s *terraform.State) error { if params == nil { return fmt.Errorf("%s: mikrotik params is nil", name) } _, ok := (*params)[key] if !ok { return fmt.Errorf("%s: attribute '%s' expected to be set", name, key) } return nil } } // testCheckNoMikrotikItemAttr ensures no value exists in MikrotikItem parameters // for the given key func testCheckNoMikrotikItemAttr(name string, params *MikrotikItem, key string) resource.TestCheckFunc { return func(s *terraform.State) error { if params == nil { return fmt.Errorf("%s: mikrotik params is nil", name) } _, ok := (*params)[key] if ok { return fmt.Errorf("%s: attribute '%s' found when not expected", name, key) } return nil } } ================================================ FILE: routeros/resource_tool_bandwidth_server.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { "allocate-udp-ports-from": "2000", "authenticate": "true", "enabled": "true", "max-sessions": "100" } */ // https://help.mikrotik.com/docs/display/ROS/Bandwidth+Test func ResourceToolBandwidthServer() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/tool/bandwidth-server"), MetaId: PropId(Id), "allowed_addresses4": { Type: schema.TypeSet, Optional: true, Description: "IPv4 allowed networks.", Elem: &schema.Schema{ Type: schema.TypeString, }, }, "allowed_addresses6": { Type: schema.TypeSet, Optional: true, Description: "Ipv6 allowed networks.", Elem: &schema.Schema{ Type: schema.TypeString, }, }, "allocate_udp_ports_from": { Type: schema.TypeInt, Optional: true, Description: "Beginning of UDP port range.", ValidateFunc: validation.IntBetween(1000, 65535), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "authenticate": { Type: schema.TypeBool, Optional: true, Description: "Communicate only with authenticated clients.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyEnabled: PropEnabled("Defines whether bandwidth server is enabled or not."), "max_sessions": { Type: schema.TypeInt, Optional: true, Description: "Maximal simultaneous test count.", ValidateFunc: validation.IntBetween(1, 1000), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_tool_bandwidth_server_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testToolsBandwidthServer = "routeros_tool_bandwidth_server.test" func TestAccToolsBandwidthServerTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccToolsBandwidthServerConfig_none(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testToolsBandwidthServer), resource.TestCheckResourceAttr(testToolsBandwidthServer, "enabled", "false"), ), }, { Config: testAccToolsBandwidthServerConfig_complex(), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(testToolsBandwidthServer, "enabled", "true"), resource.TestCheckResourceAttr(testToolsBandwidthServer, "authenticate", "false"), resource.TestCheckResourceAttr(testToolsBandwidthServer, "max_sessions", "100"), resource.TestCheckResourceAttr(testToolsBandwidthServer, "allocate_udp_ports_from", "2000"), ), }, }, }) }) } } func testAccToolsBandwidthServerConfig_none() string { return providerConfig + ` resource "routeros_tool_bandwidth_server" "test" { enabled = false } ` } func testAccToolsBandwidthServerConfig_complex() string { return providerConfig + ` resource "routeros_tool_bandwidth_server" "test" { enabled = true authenticate = false max_sessions = 100 allocate_udp_ports_from = 2000 } ` } ================================================ FILE: routeros/resource_tool_email.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { "from": "<>", "password": "", "port": "25", "server": "0.0.0.0", "tls": "no", "user": "", "vrf": "main" } */ // https://help.mikrotik.com/docs/display/ROS/E-mail func ResourceToolEmail() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/tool/e-mail"), MetaId: PropId(Id), "from": { Type: schema.TypeString, Optional: true, Description: "Name or email address that will be shown as a receiver.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "last_status": { Type: schema.TypeString, Computed: true, }, "last_address": { Type: schema.TypeString, Computed: true, }, "password": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "Password used for authenticating to an SMTP server.", }, "port": { Type: schema.TypeString, Optional: true, Description: "SMTP server's port.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "server": { Type: schema.TypeString, Optional: true, Description: "SMTP server's IP address.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "tls": { Type: schema.TypeString, Optional: true, Description: "Whether to use TLS encryption:" + "\n * yes - sends STARTTLS and drops the session if TLS is not available on the server" + "\n * no - do not send STARTTLS" + "\n * starttls - sends STARTTLS and continue without TLS if a server responds that TLS is not available", ValidateFunc: validation.StringInSlice([]string{"yes", "no", "starttls"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "user": { Type: schema.TypeString, Optional: true, Description: "The username used for authenticating to an SMTP server.", }, "vrf": PropVrfRw, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_tool_email_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testToolEmail = "routeros_tool_email.email" func TestAccToolEmailTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/tool/e-mail", "routeros_email"), Steps: []resource.TestStep{ { Config: testAccToolEmailConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testToolEmail), resource.TestCheckResourceAttr(testToolEmail, "from", "John Doe "), resource.TestCheckResourceAttr(testToolEmail, "password", "password"), resource.TestCheckResourceAttr(testToolEmail, "port", "25"), resource.TestCheckResourceAttr(testToolEmail, "server", "smtp.example.com"), resource.TestCheckResourceAttr(testToolEmail, "tls", "yes"), resource.TestCheckResourceAttr(testToolEmail, "user", "jdoe"), resource.TestCheckResourceAttr(testToolEmail, "vrf", "main"), ), }, }, }) }) } } func testAccToolEmailConfig() string { return providerConfig + ` resource "routeros_tool_email" "email" { from = "John Doe " password = "password" port = "25" server = "smtp.example.com" tls = "yes" user = "jdoe" vrf = "main" } ` } ================================================ FILE: routeros/resource_tool_graphing.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* [ { ".id": "*0", "disabled": "false", "interface": "all", "allow-address": "0.0.0.0/0", "store-on-disk": "true" }, {...} */ // https://help.mikrotik.com/docs/spaces/ROS/pages/22773810/Graphing#Graphing-Interfacegraphing func ResourceToolGraphingInterface() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/tool/graphing/interface"), MetaId: PropId(Id), KeyDisabled: PropDisabledRw, KeyInterface: PropInterfaceRw, "allow_address": { Type: schema.TypeString, Optional: true, Description: "IP address range from which is allowed to access graphing information.", ValidateFunc: ValidationIpAddress, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "store_on_disk": { Type: schema.TypeBool, Optional: true, Description: "Defines whether to store collected information on system drive.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } /* [ { ".id": "*0", "disabled": "false", "simple-queue": "all", "allow-address": "0.0.0.0/0", "allow-target": "true", "store-on-disk": "true" }, {...} */ // https://help.mikrotik.com/docs/spaces/ROS/pages/22773810/Graphing#Graphing-Queuegraphing func ResourceToolGraphingQueue() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/tool/graphing/queue"), MetaId: PropId(Id), KeyDisabled: PropDisabledRw, "simple_queue": { Type: schema.TypeString, Required: true, Description: "Defines which queues will be monitored. all means that all queues on router will be monitored.", }, "allow_address": { Type: schema.TypeString, Optional: true, Description: "IP address range from which is allowed to access graphing information.", ValidateFunc: ValidationIpAddress, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "allow_target": { Type: schema.TypeBool, Optional: true, Description: "Whether to allow access to graphs from queue's target-address.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "store_on_disk": { Type: schema.TypeBool, Optional: true, Description: "Defines whether to store collected information on system drive.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } /* [ { ".id": "*0", "disabled": "false", "allow-address": "0.0.0.0/0", "store-on-disk": "true" }, {...} */ // https://help.mikrotik.com/docs/spaces/ROS/pages/22773810/Graphing#Graphing-Resourcegraphing func ResourceToolGraphingResource() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/tool/graphing/resource"), MetaId: PropId(Id), KeyDisabled: PropDisabledRw, "allow_address": { Type: schema.TypeString, Optional: true, Description: "IP address range from which is allowed to access graphing information.", ValidateFunc: ValidationIpAddress, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "store_on_disk": { Type: schema.TypeBool, Optional: true, Description: "Defines whether to store collected information on system drive.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_tool_graphing_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testToolGraphingInterfaceTask = "routeros_tool_graphing_interface.test" func TestAccToolGraphingInterfaceTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccToolGraphingInterfaceConfig("all", "0.0.0.0/0", true), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testToolGraphingInterfaceTask), resource.TestCheckResourceAttr(testToolGraphingInterfaceTask, "interface", "all"), resource.TestCheckResourceAttr(testToolGraphingInterfaceTask, "allow_address", "0.0.0.0/0"), resource.TestCheckResourceAttr(testToolGraphingInterfaceTask, "store_on_disk", "true"), ), }, }, }) }) } } const testToolGraphingQueueTask = "routeros_tool_graphing_queue.test" func TestAccToolGraphingQueueTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccToolGraphingQueueConfig("all", "0.0.0.0/0", true, true), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testToolGraphingQueueTask), resource.TestCheckResourceAttr(testToolGraphingQueueTask, "simple_queue", "all"), resource.TestCheckResourceAttr(testToolGraphingQueueTask, "allow_address", "0.0.0.0/0"), resource.TestCheckResourceAttr(testToolGraphingQueueTask, "allow_target", "true"), resource.TestCheckResourceAttr(testToolGraphingQueueTask, "store_on_disk", "true"), ), }, }, }) }) } } const testToolGraphingResourceTask = "routeros_tool_graphing_resource.test" func TestAccToolGraphingResourceTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccToolGraphingResourceConfig("0.0.0.0/0", true), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testToolGraphingResourceTask), resource.TestCheckResourceAttr(testToolGraphingResourceTask, "allow_address", "0.0.0.0/0"), resource.TestCheckResourceAttr(testToolGraphingResourceTask, "store_on_disk", "true"), ), }, }, }) }) } } func testAccToolGraphingInterfaceConfig(_interface string, allowAddress string, storeOnDisk bool) string { return fmt.Sprintf(`%v resource "routeros_tool_graphing_interface" "test" { interface = "%v" allow_address = "%v" store_on_disk = %v } `, providerConfig, _interface, allowAddress, storeOnDisk) } func testAccToolGraphingQueueConfig(simpleQueue string, allowAddress string, allowTarget bool, storeOnDisk bool) string { return fmt.Sprintf(`%v resource "routeros_tool_graphing_queue" "test" { simple_queue = "%v" allow_address = "%v" allow_target = %v store_on_disk = %v } `, providerConfig, simpleQueue, allowAddress, allowTarget, storeOnDisk) } func testAccToolGraphingResourceConfig(allowAddress string, storeOnDisk bool) string { return fmt.Sprintf(`%v resource "routeros_tool_graphing_resource" "test" { allow_address = "%v" store_on_disk = %v } `, providerConfig, allowAddress, storeOnDisk) } ================================================ FILE: routeros/resource_tool_mac_server.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { "allowed-interface-list": "LAN" } */ // https://help.mikrotik.com/docs/display/ROS/MAC+server func ResourceToolMacServer() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/tool/mac-server"), MetaId: PropId(Id), "allowed_interface_list": { Type: schema.TypeString, Required: true, Description: "List of interfaces for MAC Telnet access.", }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } // https://help.mikrotik.com/docs/display/ROS/MAC+server func ResourceToolMacServerWinBox() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/tool/mac-server/mac-winbox"), MetaId: PropId(Id), "allowed_interface_list": { Type: schema.TypeString, Required: true, Description: "List of interfaces for MAC WinBox access.", }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } // https://help.mikrotik.com/docs/spaces/ROS/pages/98795539/MAC+server#MACserver-MACPingServer func ResourceToolMacServerPing() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/tool/mac-server/ping"), MetaId: PropId(Id), KeyEnabled: PropEnabled("Whether to enable the MAC Ping server."), } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_tool_mac_server_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testToolsMacServer = "routeros_tool_mac_server.test" func TestAccToolsMacServerTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccToolsMacServerConfig("none"), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testToolsMacServer), resource.TestCheckResourceAttr(testToolsMacServer, "allowed_interface_list", "none"), ), }, { Config: testAccToolsMacServerConfig("all"), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(testToolsMacServer, "allowed_interface_list", "all"), ), }, }, }) }) } } const testToolsMacServerWinBox = "routeros_tool_mac_server_winbox.test" func TestAccToolsMacServerWinBoxTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccToolsMacServerWinBoxConfig("none"), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testToolsMacServerWinBox), resource.TestCheckResourceAttr(testToolsMacServerWinBox, "allowed_interface_list", "none"), ), }, { Config: testAccToolsMacServerWinBoxConfig("all"), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(testToolsMacServerWinBox, "allowed_interface_list", "all"), ), }, }, }) }) } } const testToolsMacServerPing = "routeros_tool_mac_server_ping.test" func TestAccToolsMacServerPingTest_basic(t *testing.T) { for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccToolsMacServerPingConfig(true), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testToolsMacServerPing), resource.TestCheckResourceAttr(testToolsMacServerPing, "enabled", "true"), ), }, { Config: testAccToolsMacServerPingConfig(false), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(testToolsMacServerPing, "enabled", "false"), ), }, }, }) }) } } func testAccToolsMacServerConfig(acl string) string { return fmt.Sprintf(`%v resource "routeros_tool_mac_server" "test" { allowed_interface_list = "%v" } `, providerConfig, acl) } func testAccToolsMacServerWinBoxConfig(acl string) string { return fmt.Sprintf(`%v resource "routeros_tool_mac_server_winbox" "test" { allowed_interface_list = "%v" } `, providerConfig, acl) } func testAccToolsMacServerPingConfig(enabled bool) string { return fmt.Sprintf(`%v resource "routeros_tool_mac_server_ping" "test" { enabled = %v } `, providerConfig, enabled) } ================================================ FILE: routeros/resource_tool_netwatch.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "disabled": "true", "down-script": "", "host": "192.168.180.1", "http-codes": "", "name": "111", "status": "unknown", "test-script": "", "type": "simple", "up-script": "" } */ // https://help.mikrotik.com/docs/display/ROS/Netwatch func ResourceToolNetwatch() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/tool/netwatch"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields( // Generic "since", "status", "done_tests", "failed_tests", // ICMP "sent_count", "response_count", "loss_count", "loss_percent", "rtt_avg", "rtt_min", "rtt_max", "rtt_jitter", "rtt_stdev", // TCP "tcp_connect_time", // HTTP, HTTPS "http_status_code", "http_codes", // DNS "ip", "ip6", "mail_servers", "name_servers", ), KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "down_script": { Type: schema.TypeString, Optional: true, Description: "Script to execute on the event of probe state change `OK` --> `fail`.", }, "early_failure_detection": { Type: schema.TypeBool, Optional: true, Description: "Netwatch will not wait to finish all the packets to be processed to change probe status if " + "it is already known that host will be considered as `down`.", }, "early_success_detection": { Type: schema.TypeBool, Optional: true, Description: "Netwatch will not wait to finish all the packets to be processed to change probe status if " + "it is already known that host will be considered as `up`.", }, "host": { Type: schema.TypeString, Required: true, Description: "The IP address of the server to be probed. Formats:" + "\n * ipv4" + "\n * ipv4@vrf" + "\n * ipv6 " + "\n * ipv6@vrf" + "\n * ipv6-linklocal%interface", }, "interval": { Type: schema.TypeString, Optional: true, Description: "The time interval between probe tests.", DiffSuppressFunc: TimeEqual, }, KeyName: PropName("Task name."), "src_address": { Type: schema.TypeString, Optional: true, Description: "Source IP address which the Netwatch will try to use in order to reach the host. If address " + "is not present, then the host will be considered as `down`.", }, "start_delay": { Type: schema.TypeString, Optional: true, Description: "Time to wait before starting probe (on add, enable, or system start).", DiffSuppressFunc: TimeEqual, }, "startup_delay": { Type: schema.TypeString, Optional: true, Description: "Time to wait until starting Netwatch probe after system startup.", DiffSuppressFunc: TimeEqual, }, "test_script": { Type: schema.TypeString, Optional: true, Description: "Script to execute at the end of every probe test.", }, "timeout": { Type: schema.TypeString, Optional: true, Description: "Max time limit to wait for a response.", DiffSuppressFunc: TimeEqual, }, "type": { Type: schema.TypeString, Optional: true, Description: "Type of the probe:" + "\n * icmp - (ping-style) series of ICMP request-response with statistics" + "\n * tcp-conn - test TCP connection (3-way handshake) to a server specified by IP and port" + "\n * http-get - do an HTTP Get request and test for a range of correct replies" + "\n * simple - simplified ICMP probe, with fewer options than **ICMP** type, used for backward " + "compatibility with the older Netwatch version", ValidateFunc: validation.StringInSlice([]string{"icmp", "tcp-conn", "http-get", "simple"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "up_script": { Type: schema.TypeString, Optional: true, Description: "Script to execute on the event of probe state change `fail` --> `OK`.", }, // ICMP probe options "accept_icmp_time_exceeded": { Type: schema.TypeBool, Optional: true, Description: "If the ICMP `time exceeded` message should be considered a valid response.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "packet_count": { Type: schema.TypeInt, Optional: true, Description: "Total count of ICMP packets to send out within a single test.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "packet_interval": { Type: schema.TypeString, Optional: true, Description: "The time between ICMP-request packet send.", DiffSuppressFunc: TimeEqual, }, "packet_size": { Type: schema.TypeInt, Optional: true, Description: "The total size of the IP ICMP packet.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "thr_loss_count": { Type: schema.TypeInt, Optional: true, Description: "Fail threshold for loss-count.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "thr_loss_percent": { Type: schema.TypeFloat, Optional: true, Description: "Fail threshold for loss-percent.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "thr_avg": { Type: schema.TypeString, Optional: true, Description: "Fail threshold for rtt-avg.", DiffSuppressFunc: TimeEqual, }, "thr_jitter": { Type: schema.TypeString, Optional: true, Description: "Fail threshold for rtt-jitter.", DiffSuppressFunc: TimeEqual, }, "thr_max": { Type: schema.TypeString, Optional: true, Description: "Fail threshold for rtt-max (a value above thr-max is a probe fail).", DiffSuppressFunc: TimeEqual, }, "thr_stdev": { Type: schema.TypeString, Optional: true, Description: "Fail threshold for rtt-stdev.", DiffSuppressFunc: TimeEqual, }, "ttl": { Type: schema.TypeInt, Optional: true, Description: "Manually set time to live value for ICMP packet.", }, // TCP-CONNECT/HTTP-GET probe options "port": { Type: schema.TypeInt, Optional: true, Description: "TCP port (for both tcp-conn and http-get probes)", ValidateFunc: Validation64k, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, // TCP-CONNECT pass-fail criteria "thr_tcp_conn_time": { Type: schema.TypeString, Optional: true, Description: "Fail threshold for tcp-connect-time, the configuration uses microseconds, if the time " + "unit is not specified (s/m/h), log and status pages display the same value in milliseconds.", DiffSuppressFunc: TimeEqual, }, // HTTP-GET probe pass/fail criteria "thr_http_time": { Type: schema.TypeString, Optional: true, Description: "Fail threshold for http-resp-time.", DiffSuppressFunc: TimeEqual, }, "http_code_min": { Type: schema.TypeInt, Optional: true, Description: "OK/fail criteria for HTTP response code.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "http_code_max": { Type: schema.TypeInt, Optional: true, Description: "Response in the range [http-code-min , http-code-max] is a probe pass/OK; outside - a " + "probe fail. See [mozilla-http-status](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status) or " + "[rfc7231](https://datatracker.ietf.org/doc/html/rfc7231#section-6).", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, // DNS probe options "record_type": { Type: schema.TypeString, Optional: true, Description: "Record type that will be used for DNS probe.", ValidateFunc: validation.StringInSlice([]string{"A", "AAAA", "MX", "NS"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "dns_server": { Type: schema.TypeString, Optional: true, Description: "The DNS server that the probe should send its requests to, if not specified it will use the value from `/ip dns`.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_tool_netwatch_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testToolNetwatch = "routeros_tool_netwatch.test" func TestAccToolNetwatchTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccToolNetwatchConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testToolNetwatch), resource.TestCheckResourceAttr(testToolNetwatch, "name", "watch-google-pdns"), resource.TestCheckResourceAttr(testToolNetwatch, "host", "8.8.8.8"), resource.TestCheckResourceAttr(testToolNetwatch, "interval", "30s"), resource.TestCheckResourceAttr(testToolNetwatch, "up_script", ":log info \"Ping to 8.8.8.8 successful\""), ), }, { Config: testAccToolNetwatchConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testToolNetwatch), resource.TestCheckResourceAttr(testToolNetwatch, "name", "watch-google-pdns"), resource.TestCheckResourceAttr(testToolNetwatch, "host", "8.8.8.8"), resource.TestCheckResourceAttr(testToolNetwatch, "interval", "30s"), resource.TestCheckResourceAttr(testToolNetwatch, "up_script", ":log info \"Ping to 8.8.8.8 successful\""), ), }, }, }) }) } } func testAccToolNetwatchConfig() string { return fmt.Sprintf(`%v resource "routeros_tool_netwatch" "test" { name = "watch-google-pdns" type = "icmp" host = "8.8.8.8" interval = "30s" up_script = ":log info \"Ping to 8.8.8.8 successful\"" thr_max = "400ms" } `, providerConfig) } ================================================ FILE: routeros/resource_tool_sniffer.go ================================================ package routeros import ( "context" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { "file-limit": "1000", "file-name": "", "filter-cpu": "", "filter-direction": "any", "filter-dst-ip-address": "", "filter-dst-ipv6-address": "", "filter-dst-mac-address": "", "filter-dst-port": "", "filter-interface": "", "filter-ip-address": "", "filter-ip-protocol": "", "filter-ipv6-address": "", "filter-mac-address": "", "filter-mac-protocol": "", "filter-operator-between-entries": "or", "filter-port": "", "filter-size": "", "filter-src-ip-address": "", "filter-src-ipv6-address": "", "filter-src-mac-address": "", "filter-src-port": "", "filter-stream": "false", "filter-vlan": "", "memory-limit": "100", "memory-scroll": "true", "only-headers": "false", "quick-rows": "20", "quick-show-frame": "false", "running": "false", "streaming-enabled": "false", "streaming-server": "0.0.0.0:37008" } */ // https://help.mikrotik.com/docs/display/ROS/Packet+Sniffer func ResourceToolSniffer() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/tool/sniffer"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("quick_rows", "quick_show_frame", "show_frame", "enabled"), "enabled": { Type: schema.TypeBool, Optional: true, Default: true, Description: "Start packet capture.", }, "file_limit": { Type: schema.TypeInt, Optional: true, Description: "File size limit. Sniffer will stop when a limit is reached.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "file_name": { Type: schema.TypeString, Optional: true, Description: "Name of the file where sniffed packets will be saved.", }, "filter_cpu": { Type: schema.TypeString, Optional: true, Description: "CPU core used as a filter.", }, "filter_direction": { Type: schema.TypeString, Optional: true, Description: "Specifies which direction filtering will be applied.", ValidateFunc: validation.StringInSlice([]string{"any", "rx", "tx"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "filter_dst_mac_address": { Type: schema.TypeSet, Optional: true, Description: "Up to 16 MAC destination addresses and MAC address masks used as a filter.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.IsMACAddress, }, MaxItems: 16, }, "filter_dst_ip_address": { Type: schema.TypeSet, Optional: true, Description: "Up to 16 IP destination addresses used as a filter.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.IsCIDR, }, MaxItems: 16, }, "filter_dst_ipv6_address": { Type: schema.TypeSet, Optional: true, Description: "Up to 16 IPv6 destination addresses used as a filter.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.IsCIDR, }, MaxItems: 16, }, "filter_dst_port": { Type: schema.TypeSet, Optional: true, Description: "Up to 16 comma-separated destination ports used as a filter. A list of predefined port names " + "is also available, like ssh and telnet.", Elem: &schema.Schema{ Type: schema.TypeString, }, MaxItems: 16, }, "filter_interface": { Type: schema.TypeSet, Optional: true, Description: "Interface name on which sniffer will be running. all indicates that the sniffer will sniff " + "packets on all interfaces.", Elem: &schema.Schema{ Type: schema.TypeString, }, MaxItems: 16, }, "filter_ip_address": { Type: schema.TypeSet, Optional: true, Description: "Up to 16 IP addresses used as a filter.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.IsCIDR, }, MaxItems: 16, }, "filter_ipv6_address": { Type: schema.TypeSet, Optional: true, Description: "Up to 16 IPv6 addresses used as a filter.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.IsCIDR, }, MaxItems: 16, }, "filter_ip_protocol": { Type: schema.TypeSet, Optional: true, Description: "Up to 16 comma-separated IP/IPv6 protocols used as a filter. IP protocols (instead of protocol " + "names, protocol numbers can be used):" + "\n * ipsec-ah - IPsec AH protocol" + "\n * ipsec-esp - IPsec ESP protocol" + "\n * ddp - datagram delivery protocol" + "\n * egp - exterior gateway protocol" + "\n * ggp - gateway-gateway protocol" + "\n * gre - general routing encapsulation" + "\n * hmp - host monitoring protocol" + "\n * idpr-cmtp - idpr control message transport" + "\n * icmp - internet control message protocol" + "\n * icmpv6 - internet control message protocol v6" + "\n * igmp - internet group management protocol" + "\n * ipencap - ip encapsulated in ip" + "\n * ipip - ip encapsulation" + "\n * encap - ip encapsulation" + "\n * iso-tp4 - iso transport protocol class 4" + "\n * ospf - open shortest path first" + "\n * pup - parc universal packet protocol" + "\n * pim - protocol independent multicast" + "\n * rspf - radio shortest path first" + "\n * rdp - reliable datagram protocol" + "\n * st - st datagram mode" + "\n * tcp - transmission control protocol" + "\n * udp - user datagram protocol" + "\n * vmtp versatile message transport" + "\n * vrrp - virtual router redundancy protocol" + "\n * xns-idp - xerox xns idp" + "\n * xtp - xpress transfer protocol", Elem: &schema.Schema{ Type: schema.TypeString, ValidateDiagFunc: ValidationValInSlice([]string{}, false, true), }, MaxItems: 16, }, "filter_mac_address": { Type: schema.TypeSet, Optional: true, Description: "Up to 16 MAC addresses and MAC address masks used as a filter.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.IsMACAddress, }, MaxItems: 16, }, "filter_mac_protocol": { Type: schema.TypeSet, Optional: true, Description: "Up to 16 comma separated entries used as a filter. Mac protocols (instead of protocol names, " + "protocol number can be used):" + "\n * 802.2 - 802.2 Frames (0x0004)" + "\n * arp - Address Resolution Protocol (0x0806)" + "\n * homeplug-av - HomePlug AV MME (0x88E1)" + "\n * ip - Internet Protocol version 4 (0x0800)" + "\n * ipv6 - Internet Protocol Version 6 (0x86DD)" + "\n * ipx - Internetwork Packet Exchange (0x8137)" + "\n * lldp - Link Layer Discovery Protocol (0x88CC)" + "\n * loop-protect - Loop Protect Protocol (0x9003)" + "\n * mpls-multicast - MPLS multicast (0x8848)" + "\n * mpls-unicast - MPLS unicast (0x8847)" + "\n * packing-compr - Encapsulated packets with compressed IP packing (0x9001)" + "\n * packing-simple - Encapsulated packets with simple IP packing (0x9000)" + "\n * pppoe - PPPoE Session Stage (0x8864)" + "\n * pppoe-discovery - PPPoE Discovery Stage (0x8863)" + "\n * rarp - Reverse Address Resolution Protocol (0x8035)" + "\n * service-vlan - Provider Bridging (IEEE 802.1ad) & Shortest Path Bridging IEEE 802.1aq (0x88A8)" + "\n * vlan - VLAN-tagged frame (IEEE 802.1Q) and Shortest Path Bridging IEEE 802.1aq with NNI compatibility (0x8100)", Elem: &schema.Schema{ Type: schema.TypeString, }, MaxItems: 16, }, "filter_operator_between_entries": { Type: schema.TypeString, Optional: true, Description: "Changes the logic for filters with multiple entries.", ValidateFunc: validation.StringInSlice([]string{"and", "or"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "filter_port": { Type: schema.TypeSet, Optional: true, Description: "Up to 16 comma-separated ports used as a filter. A list of predefined port names is also available, " + "like ssh and telnet.", Elem: &schema.Schema{ Type: schema.TypeString, }, MaxItems: 16, }, // Type attribute string instead of number because MT returns an empty string value. "filter_size": { Type: schema.TypeString, Optional: true, Description: "Filters packets of specified size or size range in bytes.", }, "filter_src_mac_address": { Type: schema.TypeSet, Optional: true, Description: "Up to 16 MAC source addresses and MAC address masks used as a filter.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.IsMACAddress, }, MaxItems: 16, }, "filter_src_ip_address": { Type: schema.TypeSet, Optional: true, Description: "Up to 16 IP source addresses used as a filter.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.IsCIDR, }, MaxItems: 16, }, "filter_src_ipv6_address": { Type: schema.TypeSet, Optional: true, Description: "Up to 16 IPv6 source addresses used as a filter.", Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.IsCIDR, }, MaxItems: 16, }, "filter_src_port": { Type: schema.TypeSet, Optional: true, Description: "Up to 16 comma-separated source ports used as a filter. A list of predefined port names is " + "also available, like ssh and telnet.", Elem: &schema.Schema{ Type: schema.TypeString, }, MaxItems: 16, }, "filter_stream": { Type: schema.TypeBool, Optional: true, Description: "Sniffed packets that are devised for the sniffer server are ignored.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "filter_vlan": { Type: schema.TypeSet, Optional: true, Description: "Up to 16 VLAN IDs used as a filter.", Elem: &schema.Schema{ Type: schema.TypeInt, ValidateFunc: validation.IntBetween(0, 4095), }, }, "max_packet_size": { Type: schema.TypeInt, Optional: true, Description: "", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "memory_limit": { Type: schema.TypeInt, Optional: true, Description: "Memory amount used to store sniffed data.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "memory_scroll": { Type: schema.TypeBool, Optional: true, Description: "Whether to rewrite older sniffed data when the memory limit is reached.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "only_headers": { Type: schema.TypeBool, Optional: true, Description: "Save in the memory only the packet's headers, not the whole packet.", }, KeyRunning: PropRunningRo, "streaming_enabled": { Type: schema.TypeBool, Optional: true, Description: "Defines whether to send sniffed packets to the streaming server.", }, "streaming_server": { Type: schema.TypeString, Optional: true, Description: "Tazmen Sniffer Protocol (TZSP) stream receiver.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { diags := SystemResourceCreateUpdate(ctx, resSchema, d, m) if diags.HasError() { return diags } setSnifferState(ctx, resSchema, d, m) return SystemResourceRead(ctx, resSchema, d, m) }, ReadContext: func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { if d := SystemResourceRead(ctx, resSchema, d, m); d.HasError() { return d } d.Set("enabled", d.Get(KeyRunning).(bool)) return nil }, UpdateContext: func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { stopSniffer(ctx, resSchema, d, m) diags := SystemResourceCreateUpdate(ctx, resSchema, d, m) if diags.HasError() { return diags } setSnifferState(ctx, resSchema, d, m) return SystemResourceRead(ctx, resSchema, d, m) }, DeleteContext: func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { stopSniffer(ctx, resSchema, d, m) return SystemResourceDelete(ctx, resSchema, d, m) }, Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } func setSnifferState(ctx context.Context, s map[string]*schema.Schema, d *schema.ResourceData, m interface{}) diag.Diagnostics { if d.Get("enabled").(bool) { return startSniffer(ctx, s, d, m) } return stopSniffer(ctx, s, d, m) } func startSniffer(ctx context.Context, s map[string]*schema.Schema, d *schema.ResourceData, m interface{}) diag.Diagnostics { // Start sniffer. var resUrl = &URL{ Path: s[MetaResourcePath].Default.(string), } if m.(Client).GetTransport() == TransportREST { resUrl.Path += "/start" } err := m.(Client).SendRequest(crudStart, resUrl, MikrotikItem{}, nil) if err != nil { return diag.FromErr(err) } return nil } func stopSniffer(ctx context.Context, s map[string]*schema.Schema, d *schema.ResourceData, m interface{}) diag.Diagnostics { // Stop sniffer. var resUrl = &URL{ Path: s[MetaResourcePath].Default.(string), } if m.(Client).GetTransport() == TransportREST { resUrl.Path += "/stop" } err := m.(Client).SendRequest(crudStop, resUrl, MikrotikItem{}, nil) if err != nil { return diag.FromErr(err) } return nil } ================================================ FILE: routeros/resource_tool_sniffer_test.go ================================================ package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testToolSniffer = "routeros_tool_sniffer.test" func TestAccToolSnifferTest_basic(t *testing.T) { // t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccToolSnifferConfig("or"), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testToolSniffer), resource.TestCheckResourceAttr(testToolSniffer, "streaming_enabled", "true"), resource.TestCheckResourceAttr(testToolSniffer, "streaming_server", "192.168.88.5:37008"), resource.TestCheckResourceAttr(testToolSniffer, "filter_stream", "true"), resource.TestCheckResourceAttr(testToolSniffer, "filter_interface.0", "ether3"), resource.TestCheckResourceAttr(testToolSniffer, "filter_direction", "rx"), resource.TestCheckResourceAttr(testToolSniffer, "filter_operator_between_entries", "or"), ), }, { Config: testAccToolSnifferConfig("and"), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testToolSniffer), resource.TestCheckResourceAttr(testToolSniffer, "streaming_enabled", "true"), resource.TestCheckResourceAttr(testToolSniffer, "streaming_server", "192.168.88.5:37008"), resource.TestCheckResourceAttr(testToolSniffer, "filter_stream", "true"), resource.TestCheckResourceAttr(testToolSniffer, "filter_interface.0", "ether3"), resource.TestCheckResourceAttr(testToolSniffer, "filter_direction", "rx"), resource.TestCheckResourceAttr(testToolSniffer, "filter_operator_between_entries", "and"), ), }, }, }) }) } } func testAccToolSnifferConfig(param string) string { return fmt.Sprintf(`%v resource "routeros_tool_sniffer" "test" { streaming_enabled = true streaming_server = "192.168.88.5:37008" filter_stream = true filter_interface = ["ether3"] filter_direction = "rx" filter_operator_between_entries = "%v" } `, providerConfig, param) } ================================================ FILE: routeros/resource_user_manager_advanced.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { "paypal-allow": "false", "paypal-currency": "USD", "paypal-password": "", "paypal-signature": "", "paypal-use-sandbox": "false", "paypal-user": "", "web-private-password": "", "web-private-username": "" } */ // https://help.mikrotik.com/docs/display/ROS/User+Manager#UserManager-Advanced func ResourceUserManagerAdvanced() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/user-manager/advanced"), MetaId: PropId(Id), "paypal_allow": { Type: schema.TypeBool, Optional: true, Default: false, Description: "An option whether to enable PayPal functionality for User Manager.", }, "paypal_currency": { Type: schema.TypeString, Optional: true, Default: "USD", Description: "The currency related to price setting in which users will be billed.", }, "paypal_password": { Type: schema.TypeString, Optional: true, Description: "The password of the PayPal API account.", }, "paypal_signature": { Type: schema.TypeString, Optional: true, Description: "The signature of the PayPal API account.", }, "paypal_use_sandbox": { Type: schema.TypeBool, Optional: true, Default: false, Description: "An option whether to use PayPal's sandbox environment for testing purposes.", }, "paypal_user": { Type: schema.TypeString, Optional: true, Description: "The username of the PayPal API account.", }, "web_private_password": { Type: schema.TypeString, Optional: true, Description: "The password for accessing `/um/PRIVATE/` section over HTTP.", }, "web_private_username": { Type: schema.TypeString, Optional: true, Description: "The username for accessing `/um/PRIVATE/` section over HTTP.", }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_user_manager_attribute.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "default": "true", "default-name": "Framed-IP-Address", "name": "Framed-IP-Address", "packet-types": "access-accept", "standard-name": "Framed-IP-Address", "type-id": "8", "value-type": "ip-address", "vendor-id": "standard" } */ // https://help.mikrotik.com/docs/display/ROS/User+Manager#UserManager-Attributes func ResourceUserManagerAttribute() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/user-manager/attribute"), MetaId: PropId(Id), KeyDefault: PropDefaultRo, KeyDefaultName: PropDefaultNameRo("The attribute's default name."), KeyName: PropName("The attribute's name."), "packet_types": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"access-accept", "access-challenge"}, false), }, Description: "A set of `access-accept` and `access-challenge`.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "standard_name": { Type: schema.TypeString, Computed: true, }, "type_id": { Type: schema.TypeInt, Required: true, Description: "Attribute identification number from the specific vendor's attribute database.", }, "value_type": { Type: schema.TypeString, Optional: true, Default: "hex", Description: "The attribute's value type.", ValidateFunc: validation.StringInSlice([]string{"hex", "ip-address", "ip6-prefix", "macro", "string", "uint32"}, false), }, "vendor_id": { Type: schema.TypeString, Optional: true, Default: "standard", Description: "IANA allocated a specific enterprise identification number.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_user_manager_database.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { "db-path": "/flash/user-manager5", "db-size": "78176", "free-disk-space": "3248128" } */ // https://help.mikrotik.com/docs/display/ROS/User+Manager#UserManager-Database func ResourceUserManagerDatabase() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/user-manager/database"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("db_size", "free_disk_space"), "db_path": { Type: schema.TypeString, Required: true, Description: "Path to the location where database files will be stored.", }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_user_manager_limitation.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "download-limit": "0", "name": "test", "rate-limit-burst-rx": "0", "rate-limit-burst-threshold-rx": "0", "rate-limit-burst-threshold-tx": "0", "rate-limit-burst-time-rx": "0s", "rate-limit-burst-time-tx": "0s", "rate-limit-burst-tx": "0", "rate-limit-min-rx": "0", "rate-limit-min-tx": "0", "rate-limit-priority": "0", "rate-limit-rx": "10", "rate-limit-tx": "10", "reset-counters-interval": "disabled", "reset-counters-start-time": "1970-01-01 00:00:00", "transfer-limit": "0", "upload-limit": "0", "uptime-limit": "0s" } */ // https://help.mikrotik.com/docs/display/ROS/User+Manager#UserManager-Limitations func ResourceUserManagerLimitation() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/user-manager/limitation"), MetaId: PropId(Id), "download_limit": { Type: schema.TypeInt, Optional: true, Default: 0, Description: "The total amount of traffic a user can download in bytes.", ValidateFunc: validation.IntAtLeast(0), }, KeyName: PropName("Unique name of the limitation."), "rate_limit_burst_rx": { Type: schema.TypeInt, Optional: true, Default: 0, ValidateFunc: validation.IntAtLeast(0), }, "rate_limit_burst_tx": { Type: schema.TypeInt, Optional: true, Default: 0, ValidateFunc: validation.IntAtLeast(0), }, "rate_limit_burst_threshold_rx": { Type: schema.TypeInt, Optional: true, Default: 0, ValidateFunc: validation.IntAtLeast(0), }, "rate_limit_burst_threshold_tx": { Type: schema.TypeInt, Optional: true, Default: 0, ValidateFunc: validation.IntAtLeast(0), }, "rate_limit_burst_time_rx": { Type: schema.TypeString, Optional: true, Default: "0s", DiffSuppressFunc: TimeEqual, }, "rate_limit_burst_time_tx": { Type: schema.TypeString, Optional: true, Default: "0s", DiffSuppressFunc: TimeEqual, }, "rate_limit_min_rx": { Type: schema.TypeInt, Optional: true, Default: 0, ValidateFunc: validation.IntAtLeast(0), }, "rate_limit_min_tx": { Type: schema.TypeInt, Optional: true, Default: 0, ValidateFunc: validation.IntAtLeast(0), }, "rate_limit_priority": { Type: schema.TypeInt, Optional: true, Default: 0, ValidateFunc: validation.IntAtLeast(0), }, "rate_limit_rx": { Type: schema.TypeInt, Optional: true, Default: 0, ValidateFunc: validation.IntAtLeast(0), }, "rate_limit_tx": { Type: schema.TypeInt, Optional: true, Default: 0, ValidateFunc: validation.IntAtLeast(0), }, "reset_counters_interval": { Type: schema.TypeString, Optional: true, Default: "disabled", Description: "The interval from `reset_counters_start_time` when all associated user statistics are cleared.", ValidateFunc: validation.StringInSlice([]string{"disabled", "hourly", "daily", "weekly", "monthly"}, false), }, "reset_counters_start_time": { Type: schema.TypeString, Optional: true, Default: "1970-01-01 00:00:00", Description: "Static date and time value from which `reset_counters_interval` is calculated.", }, "transfer_limit": { Type: schema.TypeInt, Optional: true, Default: 0, Description: "The total amount of aggregated (download+upload) traffic in bytes.", ValidateFunc: validation.IntAtLeast(0), }, "upload_limit": { Type: schema.TypeInt, Optional: true, Default: 0, Description: "The total amount of traffic a user can upload in bytes.", ValidateFunc: validation.IntAtLeast(0), }, "uptime_limit": { Type: schema.TypeString, Optional: true, Default: "0s", Description: "The total amount of uptime a user can stay active.", DiffSuppressFunc: TimeEqual, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_user_manager_profile.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "name": "test", "name-for-users": "test", "override-shared-users": "off", "price": "0", "starts-when": "assigned", "validity": "unlimited" } */ // https://help.mikrotik.com/docs/display/ROS/User+Manager#UserManager-Profiles func ResourceUserManagerProfile() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/user-manager/profile"), MetaId: PropId(Id), KeyName: PropName("Unique name of the profile."), "name_for_users": { Type: schema.TypeString, Optional: true, Description: "The name that will be shown to users in the web interface.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "override_shared_users": { Type: schema.TypeString, Optional: true, Default: "off", Description: "An option whether to allow multiple sessions with the same user name.", }, "price": { Type: schema.TypeFloat, Optional: true, Default: .0, Description: "The price of the profile.", ValidateFunc: validation.FloatAtLeast(.0), }, "starts_when": { Type: schema.TypeString, Optional: true, Default: "assigned", Description: "The time when the profile becomes active (`assigned` - immediately when the profile entry is created, `first-auth` - upon first authentication request).", ValidateFunc: validation.StringInSlice([]string{"assigned", "first-auth"}, false), }, "validity": { Type: schema.TypeString, Optional: true, Default: "unlimited", Description: "The total amount of time a user can use this profile.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_user_manager_profile_limitation.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "from-time": "0s", "limitation": "test", "profile": "test", "till-time": "23h59m59s", "weekdays": "sunday,monday,tuesday,wednesday,thursday,friday,saturday" } */ // https://help.mikrotik.com/docs/display/ROS/User+Manager#UserManager-ProfileLimitations func ResourceUserManagerProfileLimitation() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/user-manager/profile-limitation"), MetaId: PropId(Id), "from_time": { Type: schema.TypeString, Optional: true, Default: "0s", Description: "Time of the day when the limitation should take place.", DiffSuppressFunc: TimeEqual, }, "limitation": { Type: schema.TypeString, Required: true, Description: "The limitation name.", }, "profile": { Type: schema.TypeString, Required: true, Description: "The profile name.", }, "till_time": { Type: schema.TypeString, Optional: true, Default: "23h59m59s", Description: "Time of the day when the limitation should end.", DiffSuppressFunc: TimeEqual, }, "weekdays": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"}, false), }, Description: "Days of the week when the limitation is active.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_user_manager_router.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*1", "address": "127.0.0.1", "coa-port": "3799", "disabled": "false", "name": "test", "shared-secret": "password" } */ // https://help.mikrotik.com/docs/display/ROS/User+Manager#UserManager-Routers func ResourceUserManagerRouter() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/user-manager/router"), MetaId: PropId(Id), "address": { Type: schema.TypeString, Required: true, Description: "IP address of the RADIUS client.", ValidateFunc: ValidationIpAddress, }, "coa_port": { Type: schema.TypeInt, Optional: true, Default: 3799, Description: "Port number of CoA (Change of Authorization) communication.", ValidateFunc: Validation64k, }, KeyDisabled: PropDisabledRw, KeyName: PropName("Unique name of the RADIUS client."), "shared_secret": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "The shared secret to secure communication.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_user_manager_settings.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { "accounting-port": "1813", "authentication-port": "1812", "certificate": "*0", "enabled": "true", "use-profiles": "false" } */ // https://help.mikrotik.com/docs/display/ROS/User+Manager#UserManager-Settings func ResourceUserManagerSettings() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/user-manager"), MetaId: PropId(Id), "accounting_port": { Type: schema.TypeInt, Optional: true, Default: 1813, Description: "Port to listen for RADIUS accounting requests.", ValidateFunc: validation.IntBetween(1, 65535), }, "authentication_port": { Type: schema.TypeInt, Optional: true, Default: 1812, Description: "Port to listen for RADIUS authentication requests.", ValidateFunc: validation.IntBetween(1, 65535), }, "certificate": { Type: schema.TypeString, Optional: true, Default: "none", Description: "Certificate for use in EAP TLS-type authentication methods.", }, KeyEnabled: PropEnabled("An option whether the User Manager functionality is enabled."), "require_message_auth": { Type: schema.TypeString, Optional: true, Description: "An option whether to require `Message-Authenticator` in received Access-Accept/Challenge/Reject messages.", DiffSuppressFunc: AlwaysPresentNotUserProvided, ValidateFunc: validation.StringInSlice([]string{"no", "yes-access-request"}, false), }, "use_profiles": { Type: schema.TypeBool, Optional: true, Default: false, Description: "An option whether to use Profiles and Limitations. When set to `false`, only User configuration is required to run User Manager.", }, } return &schema.Resource{ CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_user_manager_user.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*1", "attributes": "", "caller-id": "bind", "comment": "test", "disabled": "false", "group": "test", "name": "test", "otp-secret": "", "password": "password", "shared-users": "1" } */ // https://help.mikrotik.com/docs/display/ROS/User+Manager#UserManager-Users func ResourceUserManagerUser() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/user-manager/user"), MetaId: PropId(Id), "attributes": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "A custom set of colon-separated attributes with their values will be added to `Access-Accept` messages for users in this group.", }, "caller_id": { Type: schema.TypeString, Optional: true, Description: "Allow user's authentication with a specific Calling-Station-Id value.", }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "group": { Type: schema.TypeString, Optional: true, Description: "Name of the group the user is associated with.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyName: PropName("Username for session authentication."), "otp_secret": { Type: schema.TypeString, Optional: true, Description: "A token of a one-time code that will be attached to the password.", }, "password": { Type: schema.TypeString, Optional: true, Description: "The password of the user for session authentication.", }, "shared_users": { Type: schema.TypeInt, Optional: true, Default: 1, Description: "The total amount of sessions the user can simultaneously establish.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_user_manager_user_group.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "attributes": "Mikrotik-Wireless-Comment:Test Group,Mikrotik-Wireless-VLANID:100", "default": "false", "default-name": "", "inner-auths": "ttls-pap,ttls-chap,ttls-mschap1,ttls-mschap2,peap-mschap2", "name": "test", "outer-auths": "pap,chap,mschap1,mschap2,eap-tls,eap-ttls,eap-peap,eap-mschap2" } */ // https://help.mikrotik.com/docs/display/ROS/User+Manager#UserManager-UserGroups func ResourceUserManagerUserGroup() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/user-manager/user/group"), MetaId: PropId(Id), "attributes": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "A custom set of colon-separated attributes with their values will be added to `Access-Accept` messages for users in this group.", }, KeyDefault: PropDefaultRo, KeyDefaultName: PropDefaultNameRo("The default name of the group."), "inner_auths": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"ttls-pap", "ttls-chap", "ttls-mschap1", "ttls-mschap2", "peap-mschap2"}, false), }, Description: "A set of allowed authentication methods for tunneled authentication methods (`ttls-pap`, `ttls-chap`, `ttls-mschap1`, `ttls-mschap2`, `peap-mschap2`).", }, KeyName: PropName("Unique name of the group."), "outer_auths": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"pap", "chap", "mschap1", "mschap2", "eap-tls", "eap-ttls", "eap-peap", "eap-mschap2"}, false), }, Description: "A set of allowed authentication methods (`pap`, `chap`, `mschap1`, `mschap2`, `eap-tls`, `eap-ttls`, `eap-peap`, `eap-mschap2`).", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_user_manager_user_profile.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*1", "end-time": "unlimited", "profile": "test", "state": "running", "user": "test" } */ // https://help.mikrotik.com/docs/display/ROS/User+Manager#UserManager-UserProfiles func ResourceUserManagerUserProfile() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/user-manager/user-profile"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields("end_time", "state"), "profile": { Type: schema.TypeString, Required: true, Description: "Name of the profile to assign to the user.", }, "user": { Type: schema.TypeString, Required: true, Description: "Name of the user to use the specified profile.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_wifi.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".about": "mode: AP, SSID: wlan, channel: 2462/n", ".id": "*1", "arp": "enabled", "arp-timeout": "auto", "bound": "true", "configuration": "cfg1", "configuration.manager": "capsman", "configuration.mode": "ap", "default-name": "wifi1", "disabled": "false", "inactive": "false", "l2mtu": "1560", "mac-address": "00:00:00:00:00:00", "master": "true", "name": "wifi1", "radio-mac": "00:00:00:00:00:00", "running": "true", "security.connect-priority": "0" } */ // https://help.mikrotik.com/docs/display/ROS/WiFi#WiFi-Miscellaneousproperties // https://help.mikrotik.com/docs/display/ROS/WiFi#WiFi-Read-onlyproperties func ResourceWifi() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/wifi"), MetaId: PropId(Id), MetaTransformSet: PropTransformSet("aaa.config: aaa", "channel.config: channel", "configuration.config: configuration", "datapath.config: datapath", "interworking.config: interworking", "security.config: security", "steering.config: steering"), "aaa": { Type: schema.TypeMap, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "AAA inline settings.", ValidateDiagFunc: ValidationMapKeyNames, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyArp: PropArpRw, KeyArpTimeout: PropArpTimeoutRw, "bound": { Type: schema.TypeBool, Computed: true, Description: "A flag whether the interface is currently available for the WiFi manager.", }, "channel": { Type: schema.TypeMap, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "Channel inline settings.", ValidateDiagFunc: ValidationMapKeyNames, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "configuration": { Type: schema.TypeMap, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "Configuration inline settings.", ValidateDiagFunc: ValidationMapKeyNames, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "datapath": { Type: schema.TypeMap, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "Datapath inline settings.", ValidateDiagFunc: ValidationMapKeyNames, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyComment: PropCommentRw, KeyDefaultName: PropDefaultNameRo("The interface's default name."), "deprioritize_unii_3_4": { Type: schema.TypeBool, Optional: true, Description: "Whether to assign lower priority to channels with a control frequency of 5720 or 5825-5885 " + "MHz. These channels are unsupported by some client devices, making their automatic selection " + "undesirable. Defaults to `yes` in ETSI regulatory domains, elsewhere to `no`.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyDisabled: PropDisabledRw, "disable_running_check": { Type: schema.TypeBool, Optional: true, Description: "An option to set the running property to true if it is not disabled.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "inactive": { Type: schema.TypeBool, Computed: true, Description: "A flag whether the interface is currently inactive.", }, "interworking": { Type: schema.TypeMap, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "Interworking inline settings.", ValidateDiagFunc: ValidationMapKeyNames, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyL2Mtu: PropL2MtuRw, "mac_address": { Type: schema.TypeString, Description: "MAC address (BSSID) to use for the interface.", Optional: true, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "master": { Type: schema.TypeBool, Computed: true, Description: "A flag whether the interface is not a virtual one.", }, "master_interface": { Type: schema.TypeString, Optional: true, Description: "The corresponding master interface of the virtual one.", }, "mtu": { Type: schema.TypeInt, Optional: true, Description: "Layer3 maximum transmission unit", ValidateFunc: validation.IntBetween(32, 2290), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyName: PropName("Name of the interface."), "radio_mac": { Type: schema.TypeString, Computed: true, Description: "The MAC address of the associated radio.", }, "running": { Type: schema.TypeBool, Computed: true, Description: "A flag whether the interface has established a link to another device.", }, "security": { Type: schema.TypeMap, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "Security inline settings.", ValidateDiagFunc: ValidationMapKeyNames, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "steering": { Type: schema.TypeMap, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "Steering inline settings.", ValidateDiagFunc: ValidationMapKeyNames, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, } return &schema.Resource{ Description: `*This resource requires a minimum version of RouterOS 7.13.*`, CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_wifi_aaa.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*1", "called-format": "II-II-II-II-II-II:S", "calling-format": "AA-AA-AA-AA-AA-AA", "disabled": "false", "interim-update": "disabled", "mac-caching": "disabled", "name": "aaa1", "nas-identifier": "router", "password-format": "", "username-format": "AA:AA:AA:AA:AA:AA" } */ // https://help.mikrotik.com/docs/display/ROS/WiFi#WiFi-AAAproperties func ResourceWifiAaa() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/wifi/aaa"), MetaId: PropId(Id), "called_format": { Type: schema.TypeString, Optional: true, Description: "Format of the `Called-Station-Id` RADIUS attribute.", }, "calling_format": { Type: schema.TypeString, Optional: true, Description: "Format of the `Calling-Station-Id` RADIUS attribute.", }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "interim_update": { Type: schema.TypeString, Optional: true, Description: "Interval at which to send interim updates about traffic accounting to the RADIUS server.", }, "mac_caching": { Type: schema.TypeString, Optional: true, Description: "Time to cache RADIUS server replies when MAC address authentication is enabled.", }, KeyName: PropName("Name of the AAA profile."), "nas_identifier": { Type: schema.TypeString, Optional: true, Description: "Value of the `NAS-Identifier` RADIUS attribute.", }, "password_format": { Type: schema.TypeString, Optional: true, Description: "Format of the `User-Password` RADIUS attribute.", }, "username_format": { Type: schema.TypeString, Optional: true, Description: "Format of the `User-Name` RADIUS attribute.", }, } return &schema.Resource{ Description: `*This resource requires a minimum version of RouterOS 7.13.*`, CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_wifi_access_list.go ================================================ package routeros import ( "context" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "action": "accept", "allow-signal-out-of-range": "always", "client-isolation": "true", "disabled": "false", "interface": "2ghz", "mac-address": "00:00:00:00:00:00", "mac-address-mask": "00:00:00:00:00:00", "passphrase": "password", "radius-accounting": "true", "signal-range": "-120..-85", "ssid-regexp": "something", "time": "6m-20m,sun,sat", "vlan-id": "none" } */ // https://help.mikrotik.com/docs/display/ROS/WiFi#WiFi-AccessList.1 func ResourceWifiAccessList() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/wifi/access-list"), MetaId: PropId(Id), "action": { Type: schema.TypeString, Optional: true, Default: "accept", Description: "An action to take when a client matches.", ValidateFunc: validation.StringInSlice([]string{"accept", "reject", "query-radius"}, false), }, "allow_signal_out_of_range": { Type: schema.TypeString, Optional: true, Description: "An option that permits the client's signal to be out of the range always or for some time interval.", }, "client_isolation": { Type: schema.TypeBool, Optional: true, Description: "An option that specifies whether to deny forwarding data between clients connected to the same interface.", }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "interface": { Type: schema.TypeString, Optional: true, Description: "Interface name to compare with an interface to which the client actually connects to.", }, "last_logged_in": { Type: schema.TypeString, Computed: true, Description: "Last time this client logged in.", }, "last_logged_out": { Type: schema.TypeString, Computed: true, Description: "Last time this client logged out.", }, "mac_address": { Type: schema.TypeString, Optional: true, Description: "MAC address of the client.", ValidateFunc: ValidationMacAddress, }, "mac_address_mask": { Type: schema.TypeString, Optional: true, Description: "MAC address mask to apply when comparing clients' addresses.", }, "match_count": { Type: schema.TypeInt, Computed: true, Description: "Number of times this entry was matched.", }, KeyPlaceBefore: PropPlaceBefore, "passphrase": { Type: schema.TypeString, Optional: true, Description: "PSK passphrase for the client if some PSK authentication algorithm is used.", }, "radius_accounting": { Type: schema.TypeBool, Optional: true, Description: "An option that specifies if RADIUS traffic accounting should be used in case of RADIUS authentication of the client.", }, "signal_range": { Type: schema.TypeString, Optional: true, Description: "The range in which the client signal must fall.", }, "ssid_regexp": { Type: schema.TypeString, Optional: true, Description: "The regular expression to compare the actual SSID the client connects to.", }, "time": { Type: schema.TypeString, Optional: true, Description: "Time of the day and days of the week when the rule is applicable.", }, KeyVlanId: PropVlanIdRw("VLAN ID to use for VLAN tagging or `none`.", false), } return &schema.Resource{ Description: `*This resource requires a minimum version of RouterOS 7.13.*`, CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { resSchema[MetaSkipFields].Default = `"place_before"` defer func() { resSchema[MetaSkipFields].Default = `` }() return ResourceUpdate(ctx, resSchema, d, m) }, DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_wifi_cap.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { "caps-man-addresses": "192.168.88.1", "caps-man-certificate-common-names": "CAPsMAN-0000000", "caps-man-names": "router", "certificate": "request", "current-caps-man-address": "192.168.88.1", "current-caps-man-identity": "router", "discovery-interfaces": "lan", "enabled": "no", "lock-to-caps-man": "true", "slaves-datapath": "lan", "slaves-static": "true" } */ // https://help.mikrotik.com/docs/display/ROS/WiFi#WiFi-CAPconfiguration func ResourceWifiCap() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/wifi/cap"), MetaId: PropId(Name), "caps_man_addresses": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.IsIPAddress, }, Description: "List of Manager IP addresses that CAP will attempt to contact during discovery.", }, "caps_man_certificate_common_names": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "List of manager certificate common names that CAP will connect to.", }, "caps_man_names": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "An ordered list of CAPs Manager names that the CAP will connect to.", }, "certificate": { Type: schema.TypeString, Optional: true, Description: "Certificate to use for authentication.", }, "current_caps_man_address": { Type: schema.TypeString, Computed: true, Description: "Currently used CAPsMAN address.", }, "current_caps_man_identity": { Type: schema.TypeString, Computed: true, Description: "Currently used CAPsMAN identity.", }, "discovery_interfaces": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "List of interfaces over which CAP should attempt to discover CAPs Manager.", }, KeyEnabled: PropEnabled("Disable or enable the CAP functionality."), "lock_to_caps_man": { Type: schema.TypeBool, Optional: true, Description: "Lock CAP to the first CAPsMAN it connects to.", }, "locked_caps_man_common_name": { Type: schema.TypeString, Computed: true, Description: "Common name of the CAPsMAN that the CAP is locked to.", }, "requested_certificate": { Type: schema.TypeString, Computed: true, Description: "Requested certificate.", }, "slaves_datapath": { Type: schema.TypeString, Optional: true, Description: "Name of the bridge interface the CAP will be added to.", }, "slaves_static": { Type: schema.TypeBool, Optional: true, Description: "An option that creates static virtual interfaces.", }, } return &schema.Resource{ Description: `*This resource requires a minimum version of RouterOS 7.13.*`, CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_wifi_capsman.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { "ca-certificate": "auto", "certificate": "auto", "enabled": "yes", "generated-ca-certificate": "WiFi-CAPsMAN-CA-000000000000", "generated-certificate": "WiFi-CAPsMAN-000000000000", "interfaces": "LAN", "package-path": "/upgrade", "require-peer-certificate": "true", "upgrade-policy": "suggest-same-version" } */ // https://help.mikrotik.com/docs/display/ROS/WiFi#WiFi-CAPsMANGlobalConfiguration func ResourceWifiCapsman() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/wifi/capsman"), MetaId: PropId(Name), "ca_certificate": { Type: schema.TypeString, Optional: true, Description: "Device CA certificate.", }, "certificate": { Type: schema.TypeString, Optional: true, Description: "Device certificate.", }, KeyEnabled: PropEnabled("Disable or enable CAPsMAN functionality."), "generated_ca_certificate": { Type: schema.TypeString, Computed: true, Description: "Generated CA certificate.", }, "generated_certificate": { Type: schema.TypeString, Computed: true, Description: "Generated CAPsMAN certificate.", }, "interfaces": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "List of interfaces on which CAPsMAN will listen for CAP connections.", }, "package_path": { Type: schema.TypeString, Optional: true, Description: "Folder location for the RouterOS packages. For example, use '/upgrade' to specify the " + "upgrade folder from the files section. If empty string is set, CAPsMAN can use built-in RouterOS " + "packages, note that in this case only CAPs with the same architecture as CAPsMAN will be upgraded.", }, "require_peer_certificate": { Type: schema.TypeBool, Optional: true, Description: "Require all connecting CAPs to have a valid certificate.", }, "upgrade_policy": { Type: schema.TypeString, Optional: true, Description: "Upgrade policy options.", ValidateFunc: validation.StringInSlice([]string{"none", "require-same-version", "suggest-same-version"}, false), }, } return &schema.Resource{ Description: `*This resource requires a minimum version of RouterOS 7.13.*`, CreateContext: DefaultSystemCreate(resSchema), ReadContext: DefaultSystemRead(resSchema), UpdateContext: DefaultSystemUpdate(resSchema), DeleteContext: DefaultSystemDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: resSchema, } } ================================================ FILE: routeros/resource_wifi_channel.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "band": "2ghz-n", "disabled": "false", "frequency": "2412", "name": "channel1", "secondary-frequency": "disabled", "skip-dfs-channels": "disabled", "width": "20mhz" } */ // https://help.mikrotik.com/docs/display/ROS/WiFi#WiFi-Channelproperties func ResourceWifiChannel() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/wifi/channel"), MetaId: PropId(Id), "band": { Type: schema.TypeString, Optional: true, Description: "Frequency band and wireless standard that will be used by the AP. ", ValidateFunc: validation.StringInSlice([]string{"2ghz-g", "2ghz-n", "2ghz-ax", "5ghz-a", "5ghz-ac", "5ghz-ax", "5ghz-an", "5ghz-n"}, false), }, KeyComment: PropCommentRw, "deprioritize_unii_3_4": { Type: schema.TypeBool, Optional: true, Description: "Whether to assign lower priority to channels with a control frequency of 5720 or 5825-5885 " + "MHz. These channels are unsupported by some client devices, making their automatic selection " + "undesirable. Defaults to `yes` in ETSI regulatory domains, elsewhere to `no`.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyDisabled: PropDisabledRw, "frequency": { Type: schema.TypeList, Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, Description: "Channel frequency value or range in MHz on which AP or station will operate.", }, KeyName: PropName("Name of the channel."), "reselect_interval": { Type: schema.TypeString, Optional: true, Description: "An option that specifies when the interface should rescan channel availability and select the most appropriate one to use.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "reselect_time": { Type: schema.TypeString, Optional: true, Description: "Specifies the clock time when the interface should run \"rescan channel availability\" " + "and select the most appropriate one to use. Specifying the clock time will allow the system to select " + "this time dynamically and randomly. This helps to avoid a situation when many APs at the same time scan " + "the network, select the same channel, and prefer to use it at the same time. reselect-time uses a background " + "scan. " + "\nThe reselect process will choose the most suitable channel considering the number of networks in the " + "channel, channel usage, and overlap with networks in adjacent channels. It can be used with a list of " + "frequencies defined, or with frequency not set - using all supported frequencies." + "\nExample:\n" + "\n - 01:00..01:30 → Would set the rescan of channels to run every night, once, randomly, between " + "01:00 AM to 01:30 AM, system clock time." + "\n - 14:00..14:30 → Would set the rescan of channels to run every day (after midday), once, randomly " + "between 14:00:00 to 14:30:00 (or 2 PM to 2:30 PM), system clock time.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "secondary_frequency": { Type: schema.TypeList, Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, Description: "Specifies the second frequency that will be used for 80+80MHz configuration. " + "Set it to `disabled` in order to disable 80+80MHz capability.", }, "skip_dfs_channels": { Type: schema.TypeString, Optional: true, Description: "An option to avoid using channels on which channel availability check (listening for the presence of radar signals) is required.", ValidateFunc: validation.StringInSlice([]string{"10min-cac", "all", "disabled"}, false), }, "width": { Type: schema.TypeString, Optional: true, Description: "Channel width.", ValidateFunc: validation.StringInSlice([]string{"20mhz", "20/40mhz", "20/40mhz-Ce", "20/40mhz-eC", "20/40/80mhz", "20/40/80+80mhz", "20/40/80/160mhz"}, false), }, } return &schema.Resource{ Description: `*This resource requires a minimum version of RouterOS 7.13.*`, CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_wifi_configuration.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*3", "aaa": "aaa1", "antenna-gain": "10", "beacon-interval": "1s", "chains": "0,1,2,3", "channel": "channel1", "country": "Netherlands", "datapath": "datapath1", "disabled": "false", "dtim-period": "1", "hide-ssid": "true", "interworking": "interworking1", "manager": "capsman", "mode": "ap", "multicast-enhance": "disabled", "name": "cfg1", "qos-classifier": "priority", "security": "security1", "security.connect-priority": "0", "ssid": "test", "steering": "steering1", "tx-chains": "4,5,6,7", "tx-power": "10" } */ // https://help.mikrotik.com/docs/display/ROS/WiFi#WiFi-Configurationproperties func ResourceWifiConfiguration() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/wifi/configuration"), MetaId: PropId(Id), MetaTransformSet: PropTransformSet("aaa.config: aaa", "channel.config: channel", "datapath.config: datapath", "interworking.config: interworking", "security.config: security", "steering.config: steering"), "aaa": { Type: schema.TypeMap, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "AAA inline settings.", ValidateDiagFunc: ValidationMapKeyNames, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "antenna_gain": { Type: schema.TypeInt, Optional: true, Description: "An option overrides the default antenna gain.", ValidateFunc: validation.IntBetween(0, 30), }, "beacon_interval": { Type: schema.TypeString, Optional: true, Description: "Time interval between beacon frames.", DiffSuppressFunc: TimeEqual, }, "chains": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeInt, ValidateFunc: validation.IntBetween(0, 7), }, Description: "Radio chains to use for receiving signals.", }, "channel": { Type: schema.TypeMap, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "Channel inline settings.", ValidateDiagFunc: ValidationMapKeyNames, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyComment: PropCommentRw, "country": { Type: schema.TypeString, Optional: true, Description: "An option determines which regulatory domain restrictions are applied to an interface.", }, "datapath": { Type: schema.TypeMap, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "Datapath inline settings.", ValidateDiagFunc: ValidationMapKeyNames, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "deprioritize_unii_3_4": { Type: schema.TypeBool, Optional: true, Description: "Whether to assign lower priority to channels with a control frequency of 5720 or 5825-5885 " + "MHz. These channels are unsupported by some client devices, making their automatic selection " + "undesirable. Defaults to `yes` in ETSI regulatory domains, elsewhere to `no`.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyDisabled: PropDisabledRw, "dtim_period": { Type: schema.TypeInt, Optional: true, Description: "A period at which to transmit multicast traffic, when there are client devices in power save mode connected to the AP.", ValidateFunc: validation.IntBetween(1, 255), }, "hide_ssid": { Type: schema.TypeBool, Optional: true, Description: "This property has effect only in AP mode. Setting it to yes can remove this network from " + "the list of wireless networks that are shown by some client software. Changing this setting does not " + "improve the security of the wireless network, because SSID is included in other frames sent by the AP.", }, "interworking": { Type: schema.TypeMap, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "Interworking inline settings.", ValidateDiagFunc: ValidationMapKeyNames, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "manager": { Type: schema.TypeString, Optional: true, Description: "An option to specify the remote CAP mode.", ValidateFunc: validation.StringInSlice([]string{"capsman", "capsman-or-local", "local"}, false), }, "mode": { Type: schema.TypeString, Optional: true, Description: "An option to specify the access point operational mode.", ValidateFunc: validation.StringInSlice([]string{"ap", "station", "station-bridge"}, false), }, "multicast_enhance": { Type: schema.TypeString, Optional: true, Description: "An option to enable converting every multicast-address IP or IPv6 packet into multiple unicast-addresses frames for each connected station.", ValidateFunc: validation.StringInSlice([]string{"disabled", "enabled"}, false), }, KeyName: PropName("Name of the configuration."), "qos_classifier": { Type: schema.TypeString, Optional: true, Description: "An option to specify the QoS classifier.", ValidateFunc: validation.StringInSlice([]string{"dscp-high-3-bits", "priority"}, false), }, "security": { Type: schema.TypeMap, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "Security inline settings.", ValidateDiagFunc: ValidationMapKeyNames, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "ssid": { Type: schema.TypeString, Optional: true, Description: "SSID (service set identifier) is a name broadcast in the beacons that identifies wireless network.", }, "steering": { Type: schema.TypeMap, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "Steering inline settings.", ValidateDiagFunc: ValidationMapKeyNames, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "tx_chains": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeInt, ValidateFunc: validation.IntBetween(0, 7), }, Description: "Radio chains to use for transmitting signals.", }, "tx_power": { Type: schema.TypeInt, Optional: true, Description: "A limit on the transmit power (in dBm) of the interface.", ValidateFunc: validation.IntBetween(0, 40), }, } return &schema.Resource{ Description: `*This resource requires a minimum version of RouterOS 7.13.*`, CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_wifi_datapath.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "bridge": "lan", "bridge-cost": "1", "bridge-horizon": "none", "client-isolation": "true", "disabled": "false", "interface-list": "LAN", "name": "datapath1", "vlan-id": "1" } */ // https://help.mikrotik.com/docs/display/ROS/WiFi#WiFi-Datapathproperties func ResourceWifiDatapath() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/wifi/datapath"), MetaId: PropId(Id), "bridge": { Type: schema.TypeString, Optional: true, Description: "Bridge interface to add the interface as a bridge port.", }, "bridge_cost": { Type: schema.TypeString, Optional: true, Description: "Spanning tree protocol cost of the bridge port.", }, "bridge_horizon": { Type: schema.TypeString, Optional: true, Description: "Bridge horizon to use when adding as a bridge port.", }, "client_isolation": { Type: schema.TypeBool, Optional: true, Description: "An option to toggle communication between clients connected to the same AP.", }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "interface_list": { Type: schema.TypeString, Optional: true, Description: "List to which add the interface as a member.", }, KeyName: PropName("Name of the datapath."), "traffic_processing": { Type: schema.TypeString, Optional: true, Description: "", ValidateFunc: validation.StringInSlice([]string{"on-cap", "on-capsman"}, false), DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyVlanId: PropVlanIdRw("Default VLAN ID to assign to client devices connecting to this interface.", false), } return &schema.Resource{ Description: `*This resource requires a minimum version of RouterOS 7.13.*`, CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_wifi_interworking.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "3gpp-info": "12", "authentication-types": "terms-and-conditions,terms-and-conditions", "disabled": "false", "domain-names": "example.com,www.example.com", "esr": "true", "hessid": "00:00:00:00:00:00", "hotspot20": "true", "hotspot20-dgaf": "true", "internet": "true", "ipv4-availability": "not-available", "ipv6-availability": "unknown", "name": "interworking1", "network-type": "private", "operational-classes": "10,11", "operator-names": "name1,name2", "realms": "something1:not-specified,something2:eap-sim", "roaming-ois": "something1,something2", "uesa": "true", "venue": "unspecified", "venue-names": "name1,name2", "wan-at-capacity": "true", "wan-downlink": "10", "wan-downlink-load": "5", "wan-measurement-duration": "10", "wan-status": "reserved", "wan-symmetric": "true", "wan-uplink": "2", "wan-uplink-load": "1" } */ // https://help.mikrotik.com/docs/display/ROS/Interworking+Profiles func ResourceWifiInterworking() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/wifi/interworking"), MetaId: PropId(Id), "3gpp_info": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "Cellular network advertisement information - country and network codes.", }, "3gpp_raw": { Type: schema.TypeString, Optional: true, Description: "Cellular network advertisement information - country and network codes.", }, "asra": { Type: schema.TypeBool, Optional: true, Description: "An option to enable Additional Steps Required for Access.", }, "authentication_types": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "A list of authentication types that is only effective when `asra` is set to yes.", }, KeyComment: PropCommentRw, "connection_capabilities": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "A list to provide information about the allowed IP protocols and ports.", }, KeyDisabled: PropDisabledRw, "domain_names": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "A list of fully qualified domain names (FQDN) that indicate the entity operating the Hotspot.", }, "esr": { Type: schema.TypeBool, Optional: true, Description: "An option to enable Emergency Services Reachability.", }, "hessid": { Type: schema.TypeString, Optional: true, Description: "Homogenous extended service set identifier (HESSID).", }, "hotspot20": { Type: schema.TypeBool, Optional: true, Description: "An option to indicate Hotspot 2.0 capability of the Access Point.", }, "hotspot20_dgaf": { Type: schema.TypeBool, Optional: true, Description: "An option to indicate Downstream Group-Addressed Forwarding (DGAF) capability.", }, "internet": { Type: schema.TypeBool, Optional: true, Description: "An option to indicate Internet availability.", }, "ipv4_availability": { Type: schema.TypeString, Optional: true, Description: "An option to indicate IPv4 availability.", ValidateFunc: validation.StringInSlice([]string{"double-nated", "not-available", "port-restricted", "port-restricted-double-nated", "port-restricted-single-nated", "public", "single-nated", "unknown"}, false), }, "ipv6_availability": { Type: schema.TypeString, Optional: true, Description: "An option to indicate IPv6 availability.", ValidateFunc: validation.StringInSlice([]string{"available", "not-available", "unknown"}, false), }, KeyName: PropName("Name of the interworking profile."), "network_type": { Type: schema.TypeString, Optional: true, Description: "Information about network access type.", ValidateFunc: validation.StringInSlice([]string{"emergency-only", "personal-device", "private", "private-with-guest", "public-chargeable", "public-free", "test", "wildcard"}, false), }, "operational_classes": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeInt}, Description: "A list with information about other available bands.", }, "operator_names": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "A list of colon-separated operator names and language codes.", }, "realms": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "A list of colon-separated realm names and EAP methods.", }, "realms_raw": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "A list of 'NAI Realm Tuple' excluding 'NAI Realm Data Field Length' field.", }, "roaming_ois": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "A list of Organization Identifiers (OI).", }, "uesa": { Type: schema.TypeBool, Optional: true, Description: "An option to enable Unauthenticated Emergency Service Accessibility.", }, "venue": { Type: schema.TypeString, Optional: true, Description: "Information about the venue in which the Access Point is located.", }, "venue_names": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "A list of colon-separated venue names and language codes.", }, "wan_at_capacity": { Type: schema.TypeBool, Optional: true, Description: "An option to indicate that the Access Point or the network is at its max capacity.", }, "wan_downlink": { Type: schema.TypeInt, Optional: true, Description: "The downlink speed of the WAN connection set in kbps.", }, "wan_downlink_load": { Type: schema.TypeInt, Optional: true, Description: "The downlink load of the WAN connection measured over `wan_measurement_duration`.", ValidateFunc: validation.IntBetween(0, 255), }, "wan_measurement_duration": { Type: schema.TypeInt, Optional: true, Description: "The duration during which `wan_downlink_load` and `wan_uplink_load` are measured.", ValidateFunc: Validation64k, }, "wan_status": { Type: schema.TypeString, Optional: true, Description: "Information about the status of the Access Point's WAN connection.", ValidateFunc: validation.StringInSlice([]string{"down", "reserved", "test", "up"}, false), }, "wan_symmetric": { Type: schema.TypeBool, Optional: true, Description: "An option to indicate that the WAN link is symmetric (upload and download speeds are the same).", }, "wan_uplink": { Type: schema.TypeInt, Optional: true, Description: "The uplink speed of the WAN connection set in kbps.", }, "wan_uplink_load": { Type: schema.TypeInt, Optional: true, Description: "The uplink load of the WAN connection measured over `wan_measurement_duration`.", ValidateFunc: validation.IntBetween(0, 255), }, } return &schema.Resource{ Description: `*This resource requires a minimum version of RouterOS 7.13.*`, CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_wifi_provisioning.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "action": "create-enabled", "address-ranges": "192.168.88.1-192.168.88.100,192.168.100.1-192.168.100.100", "common-name-regexp": "test", "disabled": "false", "identity-regexp": "test", "master-configuration": "cfg1", "name-format": "cap1:", "radio-mac": "00:00:00:00:00:00", "slave-configurations": "cfg1", "supported-bands": "2ghz-n,2ghz-g" } */ // https://help.mikrotik.com/docs/display/ROS/WiFi#WiFi-CAPsMANProvisioning func ResourceWifiProvisioning() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/wifi/provisioning"), MetaId: PropId(Id), "action": { Type: schema.TypeString, Optional: true, Default: "none", Description: "Provisioning action.", ValidateFunc: validation.StringInSlice([]string{"create-disabled", "create-enabled", "create-dynamic-enabled", "none"}, false), }, "address_ranges": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "Match CAPs by IPs within configured address ranges.", }, KeyComment: PropCommentRw, "common_name_regexp": { Type: schema.TypeString, Optional: true, Description: "Regular expression to match radios by common name.", }, KeyDisabled: PropDisabledRw, "identity_regexp": { Type: schema.TypeString, Optional: true, Description: "Regular expression to match radios by router identity.", }, "master_configuration": { Type: schema.TypeString, Optional: true, Description: "If action specifies to create interfaces, then a new master interface with its configuration " + "set to this configuration profile will be created.", }, "name_format": { Type: schema.TypeString, Optional: true, Description: "Specify the format of the CAP interface name creation.", }, "radio_mac": { Type: schema.TypeString, Optional: true, Description: "MAC address of radio to be matched, empty MAC means match all MAC addresses. `00:00:00:00:00:00` is not considered empty MAC-address.", ValidateFunc: ValidationMacAddress, DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "slave_configurations": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "If action specifies to create interfaces, then a new slave interface for each configuration " + "profile in this list is created.", }, "slave_name_format": { Type: schema.TypeString, Optional: true, Description: "The name format of the slave CAP interfaces. This option is available in RouterOS starting from version 7.16.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "supported_bands": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"2ghz-ax", "2ghz-g", "2ghz-n", "5ghz-a", "5ghz-ac", "5ghz-ax", "5ghz-n"}, false), }, Description: "Match CAPs by supported modes.", }, } return &schema.Resource{ Description: `*This resource requires a minimum version of RouterOS 7.13.*`, CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_wifi_security.go ================================================ package routeros import ( "regexp" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "authentication-types": "wpa2-psk,wpa3-psk", "connect-group": "something", "connect-priority": "0", "dh-groups": "19,20,21", "disable-pmkid": "false", "disabled": "false", "eap-accounting": "true", "eap-anonymous-identity": "anonymous", "eap-certificate-mode": "verify-certificate", "eap-methods": "peap,tls", "eap-password": "", "eap-tls-certificate": "ca", "eap-username": "", "encryption": "tkip", "ft": "true", "ft-mobility-domain": "0x1", "ft-nas-identifier": "ssid", "ft-over-ds": "true", "ft-preserve-vlanid ": "true", "ft-r0-key-lifetime": "10m", "ft-reassociation-deadline": "10s", "group-encryption": "tkip", "group-key-update": "10m", "management-encryption": "cmac", "management-protection": "disabled", "name": "sec1", "passphrase": "passphrase", "sae-anti-clogging-threshold": "0", "sae-max-failure-rate": "disabled", "sae-pwe": "hunting-and-pecking", "wps": "disable" } */ // https://help.mikrotik.com/docs/display/ROS/WiFi#WiFi-SecurityProperties func ResourceWifiSecurity() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/wifi/security"), MetaId: PropId(Id), "authentication_types": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"wpa-psk", "wpa2-psk", "wpa-eap", "wpa2-eap", "wpa3-psk", "owe", "wpa3-eap", "wpa3-eap-192"}, false), }, Description: "Authentication types to enable on the interface.", }, KeyComment: PropCommentRw, "connect_group": { Type: schema.TypeString, Optional: true, Description: "APs within the same connect group do not allow more than 1 client device with the same MAC address.", }, "connect_priority": { Type: schema.TypeString, Optional: true, Description: "An option to determine how a connection is handled if the MAC address of the client device is the same as that of another active connection to another AP.", }, "dh_groups": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeInt, ValidateFunc: validation.IntInSlice([]int{19, 20, 21}), }, Description: "Identifiers of elliptic curve cryptography groups to use in SAE (WPA3) authentication.", }, "disable_pmkid": { Type: schema.TypeBool, Optional: true, Description: "An option to disable inclusion of a PMKID in EAPOL frames.", }, KeyDisabled: PropDisabledRw, "eap_accounting": { Type: schema.TypeBool, Optional: true, Description: "An option to send accounting information to RADIUS server for EAP-authenticated peers.", }, "eap_anonymous_identity": { Type: schema.TypeString, Optional: true, Description: "An option to specify anonymous identity for EAP outer authentication.", }, "eap_certificate_mode": { Type: schema.TypeString, Optional: true, Description: "A policy for handling the TLS certificate of the RADIUS server.", ValidateFunc: validation.StringInSlice([]string{"dont-verify-certificate", "no-certificates", "verify-certificate", "verify-certificate-with-crl"}, false), }, "eap_methods": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"peap", "tls", "ttls"}, false), }, Description: "A set of EAP methods to consider for authentication.", }, "eap_password": { Type: schema.TypeString, Optional: true, Description: "Password to use when the chosen EAP method requires one.", }, "eap_tls_certificate": { Type: schema.TypeString, Optional: true, Description: "Name or id of a certificate in the device's certificate store to use when the chosen EAP authentication method requires one.", }, "eap_username": { Type: schema.TypeString, Optional: true, Description: "Username to use when the chosen EAP method requires one. ", }, "encryption": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"ccmp", "ccmp-256", "gcmp", "gcmp-256", "tkip"}, false), }, Description: "A list of ciphers to support for encrypting unicast traffic.", }, "ft": { Type: schema.TypeBool, Optional: true, Description: "An option to enable 802.11r fast BSS transitions (roaming).", }, "ft_mobility_domain": { Type: schema.TypeString, Optional: true, Description: "The fast BSS transition mobility domain ID.", DiffSuppressFunc: HexEqual, }, "ft_nas_identifier": { Type: schema.TypeString, Optional: true, Description: "Fast BSS transition PMK-R0 key holder identifier.", ValidateFunc: validation.StringMatch(regexp.MustCompile(`^[0-9a-zA-Z]{2,96}$`), "Must be a string of 2 - 96 hex characters."), }, "ft_over_ds": { Type: schema.TypeBool, Optional: true, Description: "An option to enable fast BSS transitions over DS (distributed system).", }, "ft_preserve_vlanid": { Type: schema.TypeBool, Optional: true, Description: "An option to preserve VLAN ID when roaming.", }, "ft_r0_key_lifetime": { Type: schema.TypeString, Optional: true, Description: "The lifetime of the fast BSS transition PMK-R0 encryption key.", DiffSuppressFunc: TimeEqual, }, "ft_reassociation_deadline": { Type: schema.TypeString, Optional: true, Description: "Fast BSS transition reassociation deadline.", DiffSuppressFunc: TimeEqual, }, "group_encryption": { Type: schema.TypeString, Optional: true, Description: "A cipher to use for encrypting multicast traffic.", ValidateFunc: validation.StringInSlice([]string{"ccmp", "ccmp-256", "gcmp", "gcmp-256", "tkip"}, false), }, "group_key_update": { Type: schema.TypeString, Optional: true, Description: "The interval at which the group temporal key (key for encrypting broadcast traffic) is renewed.", DiffSuppressFunc: TimeEqual, }, "management_encryption": { Type: schema.TypeString, Optional: true, Description: "A cipher to use for encrypting protected management frames.", ValidateFunc: validation.StringInSlice([]string{"cmac", "cmac-256", "gmac", "gmac-256"}, false), }, "management_protection": { Type: schema.TypeString, Optional: true, Description: "An option to enable 802.11w management frame protection.", ValidateFunc: validation.StringInSlice([]string{"allowed", "disabled", "required"}, false), }, "multi_passphrase_group": { Type: schema.TypeString, Optional: true, Description: "Name of `/interface/wifi/security/multi-passphrase/` group that will be used. Only a " + "single group can be defined under the security profile.", }, "owe_transition_interface": { Type: schema.TypeString, Optional: true, Description: "Name or internal ID of an interface which MAC address and SSID to advertise as the matching AP when running in OWE transition mode.", }, KeyName: PropName("Name of the security profile."), "passphrase": { Type: schema.TypeString, Optional: true, Description: "Passphrase to use for PSK authentication types.", }, "sae_anti_clogging_threshold": { Type: schema.TypeString, Optional: true, Description: "A parameter to mitigate DoS attacks by specifying a threshold of in-progress SAE authentications.", }, "sae_max_failure_rate": { Type: schema.TypeString, Optional: true, Description: "Rate of failed SAE (WPA3) associations per minute, at which the AP will stop processing new association requests.", }, "sae_pwe": { Type: schema.TypeString, Optional: true, Description: "Methods to support for deriving SAE password element.", ValidateFunc: validation.StringInSlice([]string{"both", "hash-to-element", "hunting-and-pecking"}, false), }, "wps": { Type: schema.TypeString, Optional: true, Description: "An option to enable WPS (Wi-Fi Protected Setup).", ValidateFunc: validation.StringInSlice([]string{"disable", "push-button"}, false), }, } return &schema.Resource{ Description: `*This resource requires a minimum version of RouterOS 7.13.*`, CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_wifi_security_multi_passphrase.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "disabled": "false", "expired": "false", "group": "123", "passphrase": "12345678" } */ // https://help.mikrotik.com/docs/spaces/ROS/pages/224559120/WiFi#WiFi-Securitymulti-passphraseproperties func ResourceWifiSecurityMultiPassphrase() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/wifi/security/multi-passphrase"), MetaId: PropId(Id), KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "expires": { Type: schema.TypeString, Optional: true, Description: "The expiration date and time for passphrase specified in this entry, doesn't affect the whole " + "group. Once the date is reached, existing clients using this passphrase will be disconnected, and new " + "clients will not be able to connect using it. If not set, passphrase can be used indefinetly.", }, "group": { Type: schema.TypeString, Required: true, Description: "Assigning the group to a security profile or an access list, will enable use of all passphrases " + "defined under it.", }, "isolation": { Type: schema.TypeBool, Optional: true, Description: "Determines whether the client device using this passphrase is isolated from other clients " + "on AP. Traffic from an isolated client will not be forwarded to other clients and unicast traffic from " + "a non-isolated client will not be forwarded to an isolated one.", }, "passphrase": { Type: schema.TypeString, Optional: true, Sensitive: true, Description: "The passphrase to use for PSK authentication types. Multiple users can use the same passphrase. " + "Not compatible with WPA3-PSK.", ValidateFunc: validation.StringLenBetween(8, 64), }, KeyVlanId: PropVlanIdRw("vlan-id that will be assigned to clients using this passphrase Only supported on wifi-qcom "+ "interfaces, if wifi-qcom-ac AP has a client that uses a passphrase that has vlan-id associated with "+ "it, the client will not be able to join.", false), } return &schema.Resource{ Description: `*This resource requires a minimum version of RouterOS 7.17beta1.*`, CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_wifi_steering.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*1", "disabled": "false", "name": "steering1", "neighbor-group": "something", "rrm": "true", "wnm": "true" } */ // https://help.mikrotik.com/docs/display/ROS/WiFi#WiFi-Steeringproperties func ResourceWifiSteering() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/interface/wifi/steering"), MetaId: PropId(Id), KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, KeyName: PropName("Name of the steering profile."), "neighbor_group": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "Neighbor group of potential roaming candidates.", }, "rrm": { Type: schema.TypeBool, Optional: true, Description: "An option to enable sending 802.11k neighbor reports.", }, "wnm": { Type: schema.TypeBool, Optional: true, Description: "An option to enable sending 802.11v BSS transition management requests.", }, } return &schema.Resource{ Description: `*This resource requires a minimum version of RouterOS 7.13.*`, CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_wireguard_keys.go ================================================ package routeros import ( "context" "crypto/rand" "encoding/base64" "fmt" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "golang.org/x/crypto/curve25519" ) func ResourceWireguardKeys() *schema.Resource { return &schema.Resource{ Description: "Creating key sets for WireGuard tunnels.", Schema: map[string]*schema.Schema{ MetaId: { Type: schema.TypeInt, Optional: true, ForceNew: true, Default: int(Name), DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { return true }, }, MetaResourcePath: { Type: schema.TypeString, Optional: true, ForceNew: true, Default: "local", DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { return true }, }, "number": { Type: schema.TypeInt, Optional: true, ForceNew: true, Default: 1, Description: "The number of key sets.", }, "keys": { Type: schema.TypeList, Computed: true, Sensitive: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "preshared": { Type: schema.TypeString, Computed: true, Description: "Pre-shared secret key.", }, "private": { Type: schema.TypeString, Computed: true, Description: "Private WG key.", }, "public": { Type: schema.TypeString, Computed: true, Description: "Public WG Key.", }, }, }, }, }, CreateContext: wgKeysCreate, ReadContext: func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { return nil }, DeleteContext: func(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { d.SetId("") return nil }, } } // https://github.com/WireGuard/wgctrl-go/blob/master/wgtypes/types.go // KeyLen is the expected key length for a WireGuard key. const KeyLen = 32 // wgh.KeyLen // A Key is a public, private, or pre-shared secret key. The Key constructor // functions in this package can be used to create Keys suitable for each of // these applications. type Key [KeyLen]byte // GenerateKey generates a Key suitable for use as a pre-shared secret key from // a cryptographically safe source. // // The output Key should not be used as a private key; use GeneratePrivateKey // instead. func GenerateKey() (Key, error) { b := make([]byte, KeyLen) if _, err := rand.Read(b); err != nil { return Key{}, fmt.Errorf("failed to read random bytes: %v", err) } return NewKey(b) } // GeneratePrivateKey generates a Key suitable for use as a private key from a // cryptographically safe source. func GeneratePrivateKey() (Key, error) { key, err := GenerateKey() if err != nil { return Key{}, err } // Modify random bytes using algorithm described at: // https://cr.yp.to/ecdh.html. key[0] &= 248 key[31] &= 127 key[31] |= 64 return key, nil } // NewKey creates a Key from an existing byte slice. The byte slice must be // exactly 32 bytes in length. func NewKey(b []byte) (Key, error) { if len(b) != KeyLen { return Key{}, fmt.Errorf("incorrect key size: %d", len(b)) } var k Key copy(k[:], b) return k, nil } // PublicKey computes a public key from the private key k. // // PublicKey should only be called when k is a private key. func (k Key) PublicKey() Key { var ( pub [KeyLen]byte priv = [KeyLen]byte(k) ) // ScalarBaseMult uses the correct base value per https://cr.yp.to/ecdh.html, // so no need to specify it. curve25519.ScalarBaseMult(&pub, &priv) return Key(pub) } // String returns the base64-encoded string representation of a Key. // // ParseKey can be used to produce a new Key from this string. func (k Key) String() string { return base64.StdEncoding.EncodeToString(k[:]) } func wgKeysCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { var res []map[string]any for i := 0; i < d.Get("number").(int); i++ { keys := make(map[string]any) key, err := GeneratePrivateKey() if err != nil { return diag.FromErr(err) } keys["private"] = key.String() keys["public"] = key.PublicKey().String() key, err = GenerateKey() if err != nil { return diag.FromErr(err) } keys["preshared"] = key.String() res = append(res, keys) } d.SetId("wg_keys") return diag.FromErr(d.Set("keys", res)) } ================================================ FILE: routeros/resource_wireguard_keys_test.go ================================================ package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testResourceWireGuardKeys = "routeros_wireguard_keys.keys" func TestAccResourceWireGuardKeys_basic(t *testing.T) { // t.Parallel() t.Run("WG keys", func(t *testing.T) { resource.Test(t, resource.TestCase{ ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccResourceWireGuardKeysConfig(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testResourceWireGuardKeys), resource.TestCheckResourceAttr(testResourceWireGuardKeys, "number", "3"), ), }, }, }) }) } func testAccResourceWireGuardKeysConfig() string { return ` provider "routeros" { insecure = true } resource "routeros_wireguard_keys" "keys" { number = 3 } ` } ================================================ FILE: routeros/resource_zerotier.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) /* { ".id": "*1", "comment": "ZeroTier Central controller - https://my.zerotier.com/", "disabled": "false", "identity": "...", "identity.public": "...", "interfaces": "all", "name": "zt1", "online": "true", "port": "9993", "route-distance": "1", "state": "running" } */ // https://help.mikrotik.com/docs/display/ROS/ZeroTier#ZeroTier-Parameters func ResourceZerotier() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/zerotier"), MetaId: PropId(Id), MetaTransformSet: PropTransformSet("identity_public: identity.public"), KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "identity": { Type: schema.TypeString, Optional: true, Description: "The 40-bit unique instance address.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "identity_public": { Type: schema.TypeString, Computed: true, Description: "The public identity of the ZeroTier instance.", }, "interfaces": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "The interfaces to discover ZeroTier peers by ARP and IP type connections.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, KeyName: PropName("Name of the ZeroTier instance."), "online": { Type: schema.TypeBool, Computed: true, Description: "A flag whether the ZeroTier instance is currently online.", }, "port": { Type: schema.TypeInt, Optional: true, Default: 9993, Description: "The port number the instance listens to.", ValidateFunc: validation.IntBetween(1, 65535), }, "route_distance": { Type: schema.TypeInt, Optional: true, Default: 1, Description: "The route distance for routes obtained from the planet/moon server.", }, "state": { Type: schema.TypeString, Computed: true, Description: "The state of the ZeroTier instance.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_zerotier_controller.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*1", "broadcast": "false", "comment": "Something", "disabled": "false", "inactive": "true", "instance": "zt1", "ip-range": "192.168.88.200-192.168.88.220", "ip6-6plane": "false", "ip6-range": "fd00:feed:feed:beef::-fd00:feed:feed:beef:ffff:ffff:ffff:ffff", "ip6-rfc4193": "false", "mtu": "1598", "multicast-limit": "32", "name": "test", "network": "1234567812345678", "private": "true", "routes": "192.168.88.0/24@192.168.88.1,0.0.0.0/0@192.168.88.1" } */ // https://help.mikrotik.com/docs/display/ROS/ZeroTier#ZeroTier-Parameters.1 func ResourceZerotierController() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/zerotier/controller"), MetaId: PropId(Id), "broadcast": { Type: schema.TypeBool, Optional: true, Default: true, Description: "An option to allow receiving broadcast packets.", }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "inactive": { Type: schema.TypeBool, Computed: true, Description: "A flag whether the ZeroTier network is inactive.", }, "instance": { Type: schema.TypeString, Required: true, Description: "The ZeroTier instance name.", }, "ip_range": { Type: schema.TypeString, Optional: true, Description: "The IP range of the ZeroTier network.", }, "ip6_6plane": { Type: schema.TypeBool, Optional: true, Default: false, Description: "An option to assign every member a `/80` address within a `/40` network with using NDP emulation.", }, "ip6_range": { Type: schema.TypeString, Optional: true, Description: "The IPv6 range of the ZeroTier network.", }, "ip6_rfc4193": { Type: schema.TypeBool, Optional: true, Default: false, Description: "An option to assign every member a `/128` address within a `/88` network.", }, KeyMtu: PropL2MtuRw, "multicast_limit": { Type: schema.TypeInt, Optional: true, Default: 32, Description: "An option to limit the maximum recipients of a multicast packet.", }, KeyName: PropName("Name of the ZeroTier controller."), "network": { Type: schema.TypeString, Required: true, Description: "The ZeroTier network identifier.", }, "private": { Type: schema.TypeBool, Optional: true, Default: true, Description: "The ZeroTier network access control.", }, "routes": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: "The routes list that will be pushed to the client.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: routeros/resource_zerotier_interface.go ================================================ package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* { ".id": "*1", "allow-default": "false", "allow-global": "false", "allow-managed": "false", "arp-timeout": "auto", "bridge": "true", "dhcp": "false", "disabled": "false", "instance": "zt1", "mac-address": "00:00:00:00:00:00", "mtu": "2800", "name": "zerotier1", "network": "a00000000aa00a00", "network-name": "something", "running": "true", "status": "OK", "type": "PRIVATE" } */ // https://help.mikrotik.com/docs/display/ROS/ZeroTier#ZeroTier-Parameters func ResourceZerotierInterface() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/zerotier/interface"), MetaId: PropId(Id), "allow_default": { Type: schema.TypeBool, Optional: true, Default: false, Description: "An option to override the default route.", }, "allow_global": { Type: schema.TypeBool, Optional: true, Default: false, Description: "An option to allow overlapping public IP space by the ZeroTier routes. .", }, "allow_managed": { Type: schema.TypeBool, Optional: true, Default: false, Description: "An option to allow assignment of managed IPs.", }, KeyArpTimeout: PropArpTimeoutRw, "bridge": { Type: schema.TypeBool, Computed: true, Description: "A flag whether the ZeroTier interface is bridged.", }, KeyComment: PropCommentRw, KeyDisabled: PropDisabledRw, "disable_running_check": { Type: schema.TypeBool, Optional: true, Description: "An option to force the `running` property to true.", DiffSuppressFunc: AlwaysPresentNotUserProvided, }, "dhcp": { Type: schema.TypeBool, Computed: true, Description: "A flag whether the ZeroTier interface obtained an IP address.", }, "instance": { Type: schema.TypeString, Required: true, Description: "The ZeroTier instance name.", }, KeyMacAddress: PropMacAddressRo, KeyMtu: PropL2MtuRo, KeyName: PropName("Name of the ZeroTier interface."), "network": { Type: schema.TypeString, Required: true, Description: "The ZeroTier network identifier.", }, "network_name": { Type: schema.TypeString, Computed: true, Description: "The ZeroTier network name.", }, KeyRunning: PropRunningRo, "status": { Type: schema.TypeString, Computed: true, Description: "The status of the ZeroTier connection.", }, "type": { Type: schema.TypeString, Computed: true, Description: "The ZeroTier network type.", }, } return &schema.Resource{ CreateContext: DefaultCreate(resSchema), ReadContext: DefaultRead(resSchema), UpdateContext: DefaultUpdate(resSchema), DeleteContext: DefaultDelete(resSchema), Importer: &schema.ResourceImporter{ StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } } ================================================ FILE: templates/data-sources/firewall.md.tmpl ================================================ # {{.Name}} ({{.Type}}) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_ip_firewall](ip_firewall.md) ================================================ FILE: templates/data-sources.md.tmpl ================================================ # {{.Name}} ({{.Type}}) {{ .Description | trimspace }} {{ if .HasExample -}} ## Example Usage {{ tffile .ExampleFile }} {{- end }} {{ .SchemaMarkdown | trimspace }} {{ if .HasImport -}} ## Import Import is supported using the following syntax: {{ codefile "shell" .ImportFile }} {{- end }} ================================================ FILE: templates/guides/certificate_rotation.md ================================================ # Certificate rotation Original [issue](https://github.com/terraform-routeros/terraform-provider-routeros/issues/584) ## Example ```terraform resource "tls_private_key" "ca_key" { algorithm = "RSA" } resource "tls_self_signed_cert" "ca_cert" { subject { common_name = "testCA" organization = "test" } private_key_pem = tls_private_key.ca_key.private_key_pem allowed_uses = ["digital_signature", "cert_signing", "crl_signing"] validity_period_hours = 24 * 365 * 5 is_ca_certificate = true } resource "tls_private_key" "server_key" { algorithm = "RSA" } resource "tls_cert_request" "server_csr" { private_key_pem = tls_private_key.server_key.private_key_pem subject { common_name = "mikrotik.example.com" organization = "test" } } resource "tls_locally_signed_cert" "server_cert" { cert_request_pem = tls_cert_request.server_csr.cert_request_pem ca_private_key_pem = tls_private_key.ca_key.private_key_pem ca_cert_pem = tls_self_signed_cert.ca_cert.cert_pem validity_period_hours = 12 allowed_uses = [ "key_encipherment", "digital_signature", "server_auth", ] } output "cert_serial_number_expected" { value = format("%x", tls_locally_signed_cert.server_cert.id) } resource "routeros_file" "server_key" { name = "server.key" contents = tls_private_key.server_key.private_key_pem } resource "routeros_file" "server_cert" { name = "server.crt" contents = tls_locally_signed_cert.server_cert.cert_pem } resource “routeros_system_certificate” “server_cert” { name = “server” common_name = tls_cert_request.server_csr.subject[0].common_name import { cert_file_name = routeros_file.server_cert.name key_file_name = routeros_file.server_key.name } depends_on = [routeros_file.server_cert, routeros_file.server_key] lifecycle { replace_triggered_by = [ tls_locally_signed_cert.server_cert.cert_pem ] } } output "cert_serial_nubmer_on_device" { value = routeros_system_certificate.server_cert.serial_number } ``` ================================================ FILE: templates/guides/easy_import.md ================================================ # Install package Original [issue](https://github.com/terraform-routeros/terraform-provider-routeros/issues/488) ## Example ```shell #!/bin/bash USER=admin PASS= HOST=http://router.local i=0 curl -s -u ${USER}:${PASS} ${HOST}/rest/ip/firewall/address-list | jq -c '.[] | select(.dynamic | ascii_downcase == "false") | {index: .".id", address: .address, comment: .comment, list: .list}' | while read rec; do index=$(echo $rec | jq .index) idx=$(printf "%00004d" $i) # echo $rec bash -cv "tofu state rm 'module.dev-gw0.routeros_ip_firewall_addr_list.address_list[\"$idx\"]'" bash -cv "tofu import 'module.dev-gw0.routeros_ip_firewall_addr_list.address_list[\"$idx\"]' $index" let i=${i}+1 done ``` ```terraform variable "address_list" { type = list(object({ address = string comment = optional(string) disabled = optional(bool, false) dynamic = optional(bool, false) list = string })) default = [ { address="192.168.88.11", comment="example 2", list="srv" }, { address="192.168.88.12", comment="example 2", list="srv" }, { address="192.168.88.1", comment="example", list="routeros" }, ] locals { # https://discuss.hashicorp.com/t/does-map-sort-keys/12056/2 # Map keys are always iterated in lexicographical order! address_list_map = { for idx, rule in var.address_list : format("%00004d", idx) => rule } } resource "routeros_ip_firewall_addr_list" "address_list" { for_each = local.address_list_map address = each.value.address comment = each.value.comment disabled = each.value.disabled list = each.value.list } ``` ================================================ FILE: templates/guides/install_package.md ================================================ # Install package The original example package installation is available in the [Schwitzd](https://github.com/Schwitzd/IaC-HomeRouter/blob/main/container_backend.tf) repository. ## Example ```terraform resource "null_resource" "download_container_npk" { provisioner "local-exec" { command = < /dev/null do echo "Waiting for router to reboot and become available..." sleep 10 done EOT } depends_on = [ null_resource.upload_container_npk ] } ``` ```shell #!/bin/bash # Input parameters ARCHITECTURE_NAME=$1 VERSION=$2 PACKAGE_NAME_PREFIX=$3 # Define the base URL and package format BASE_URL="https://download.mikrotik.com/routeros" PACKAGE_FORMAT="all_packages-${ARCHITECTURE_NAME}-${VERSION}.zip" # Construct the full URL FULL_URL="${BASE_URL}/${VERSION}/${PACKAGE_FORMAT}" # Define the download and extraction paths DOWNLOAD_PATH="/tmp/${PACKAGE_FORMAT}" EXTRACT_PATH="/tmp/routeros_packages" # Download the package echo "Downloading package from: ${FULL_URL}" curl -o "${DOWNLOAD_PATH}" "${FULL_URL}" # Verify download if [ $? -ne 0 ]; then echo "Failed to download the package." exit 1 fi # Create the extraction directory mkdir -p "${EXTRACT_PATH}" # List all files in the ZIP archive and filter by the PACKAGE_NAME_PREFIX echo "Finding package that starts with: ${PACKAGE_NAME_PREFIX}" MATCHED_FILES=$(unzip -l "${DOWNLOAD_PATH}" | awk '{print $4}' | grep "^${PACKAGE_NAME_PREFIX}") # Check if any files were matched if [ -z "$MATCHED_FILES" ]; then echo "No files found starting with '${PACKAGE_NAME_PREFIX}'." exit 1 fi # Extract matched files for FILE in $MATCHED_FILES; do echo "Extracting: ${FILE}" unzip -jo "${DOWNLOAD_PATH}" "${FILE}" -d "${EXTRACT_PATH}" if [ $? -ne 0 ]; then echo "Failed to extract: ${FILE}" exit 1 fi done echo "Extraction completed successfully in: ${EXTRACT_PATH}" ``` ================================================ FILE: templates/index.md.tmpl ================================================ --- # generated by https://github.com/hashicorp/terraform-plugin-docs page_title: "RouterOS Provider" subcategory: "" description: |- A provider to integrate with the REST API introduced in RouterOS v7 --- # RouterOS Provider To get started with the provider, you first need to enable the REST API on your router. [You can follow the Mikrotik documentation on this](https://help.mikrotik.com/docs/display/ROS/REST+API), but the gist is to create an SSL cert (in `/system/certificates`) and enable the `web-ssl` service (in `/ip/services`) which uses that certificate. ## Example Usage {{tffile "examples/provider/provider.tf"}} {{ .SchemaMarkdown | trimspace }} ================================================ FILE: templates/resources/bridge.md.tmpl ================================================ # {{.Name}} ({{.Type}}) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_interface_bridge](interface_bridge.md) ================================================ FILE: templates/resources/bridge_port.md.tmpl ================================================ # {{.Name}} ({{.Type}}) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_interface_bridge_port](interface_bridge_port.md) ================================================ FILE: templates/resources/bridge_vlan.md.tmpl ================================================ # {{.Name}} ({{.Type}}) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_interface_bridge_vlan](interface_bridge_vlan.md) ================================================ FILE: templates/resources/certificate_scep_server.md.tmpl ================================================ # {{.Name}} ({{.Type}}) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_system_certificate_scep_server](system_certificate_scep_server.md) ================================================ FILE: templates/resources/dhcp_client.md.tmpl ================================================ # {{.Name}} ({{.Type}}) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_ip_dhcp_client](ip_dhcp_client.md) ================================================ FILE: templates/resources/dhcp_server.md.tmpl ================================================ # {{.Name}} ({{.Type}}) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_ip_dhcp_server](ip_dhcp_server.md) ================================================ FILE: templates/resources/dhcp_server_lease.md.tmpl ================================================ # {{.Name}} ({{.Type}}) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_ip_dhcp_server_lease](ip_dhcp_server_lease.md) ================================================ FILE: templates/resources/dhcp_server_network.md.tmpl ================================================ # {{.Name}} ({{.Type}}) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_ip_dhcp_server_network](ip_dhcp_server_network.md) ================================================ FILE: templates/resources/dns.md.tmpl ================================================ # {{.Name}} ({{.Type}}) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_ip_dns](ip_dns.md) ================================================ FILE: templates/resources/dns_record.md.tmpl ================================================ # {{.Name}} ({{.Type}}) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_ip_dns_record](ip_dns_record.md) ================================================ FILE: templates/resources/firewall_addr_list.md.tmpl ================================================ # {{.Name}} ({{.Type}}) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_ip_firewall_addr_list](ip_firewall_addr_list.md) ================================================ FILE: templates/resources/firewall_filter.md.tmpl ================================================ # {{.Name}} ({{.Type}}) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_ip_firewall_filter](ip_firewall_filter.md) ================================================ FILE: templates/resources/firewall_mangle.md.tmpl ================================================ # {{.Name}} ({{.Type}}) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_ip_firewall_mangle](ip_firewall_mangle.md) ================================================ FILE: templates/resources/firewall_nat.md.tmpl ================================================ # {{.Name}} ({{.Type}}) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_ip_firewall_nat](ip_firewall_nat.md) ================================================ FILE: templates/resources/gre.md.tmpl ================================================ # {{.Name}} ({{.Type}}) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_interface_gre](interface_gre.md) ================================================ FILE: templates/resources/identity.md.tmpl ================================================ # {{.Name}} ({{.Type}}) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_system_identity](system_identity.md) ================================================ FILE: templates/resources/ip_dhcp_server_option_set.md.tmpl ================================================ # {{.Name}} ({{.Type}}) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_ip_dhcp_server_option_sets](ip_dhcp_server_option_sets.md) ================================================ FILE: templates/resources/scheduler.md.tmpl ================================================ # {{.Name}} ({{.Type}}) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_system_scheduler](system_scheduler.md) ================================================ FILE: templates/resources/system_certificate.md.tmpl ================================================ # {{.Name}} ({{.Type}}) {{ .Description | trimspace }} Certificate resource management consists of two independent processes: * key creation and certificate signing request (`key` + `csr`) * certificate signing by the issuer (`crt`) For a complete certificate creation cycle, both of the above steps must be performed. In this case the `sign {}` block must be specified in the configuration. If you need to import the current state of the certificate resource, then do not specify the `sign{}` block. Importing an external certificate is also done without specifying the `sign{}` block, because the certificate should have already been signed by the issuer at this step. --- {{ if .HasExample -}} ## Example Usage {{ tffile .ExampleFile }} {{- end }} {{ .SchemaMarkdown | trimspace }} {{ if .HasImport -}} ## Import Import is supported using the following syntax: {{ codefile "shell" .ImportFile }} {{- end }} ================================================ FILE: templates/resources/vlan.md.tmpl ================================================ # {{.Name}} ({{.Type}}) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_interface_vlan](interface_vlan.md) ================================================ FILE: templates/resources/vrrp.md.tmpl ================================================ # {{.Name}} ({{.Type}}) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_interface_vrrp](interface_vrrp.md) ================================================ FILE: templates/resources/wireguard.md.tmpl ================================================ # {{.Name}} ({{.Type}}) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_interface_wireguard](interface_wireguard.md) ================================================ FILE: templates/resources/wireguard_peer.md.tmpl ================================================ # {{.Name}} ({{.Type}}) --- #### This is an alias for backwards compatibility between plugin versions. Please see documentation for [routeros_interface_wireguard_peer](interface_wireguard_peer.md) ================================================ FILE: templates/resources.md.tmpl ================================================ # {{.Name}} ({{.Type}}) {{ .Description | trimspace }} {{ if .HasExample -}} ## Example Usage {{ tffile .ExampleFile }} {{- end }} {{ .SchemaMarkdown | trimspace }} {{ if .HasImport -}} ## Import Import is supported using the following syntax: {{ codefile "shell" .ImportFile }} {{- end }} ================================================ FILE: tools/boilerplate/main.go ================================================ // go run tools/boilerplate/main.go routeros_interface_lte_apn package main import ( "bufio" "bytes" "flag" "fmt" "log" "os" "path/filepath" "regexp" "strconv" "strings" "text/template" "unicode" "github.com/fatih/color" ) var ( reNewItemName = regexp.MustCompile(`^routeros_[a-z0-9_]+$`) isDS = flag.Bool("ds", false, "This is a datasource") isSystem = flag.Bool("system", false, "This is a system resource") csvTable = flag.String("table", "", "Extracting attributes from the WIKI table (CSV file)") fromCsvName = flag.Bool("from-csv", false, "Generate resource name from CSV file name routeros_csv_file_name") ) func Fatalf(format string, a ...any) { _, _ = fmt.Fprintf(os.Stderr, format, a...) _, _ = fmt.Fprintln(os.Stderr, "") os.Exit(1) } type ItemType byte const ( Resource ItemType = iota + 1 Datasource ) func (t ItemType) String() string { switch t { case Resource: return "Resource" case Datasource: return "Datasource" default: panic("Unknown ItemType") } } func (t ItemType) HCL() string { switch t { case Resource: return "resource" case Datasource: return "datasource" default: panic("Unknown ItemType") } } func main() { flag.Parse() if len(flag.Args()) < 1 && !*fromCsvName { Fatalf(` Usage: go run tools/bolerplate/main.go [-from-csv] [-table file.csv] [-system] [routeros_new_resource] go run main.go -from-csv -table ip_ipsec_key.csv `) } var resName string if len(flag.Args()) > 0 { resName = flag.Args()[0] } if !reNewItemName.MatchString(resName) { if !*fromCsvName { Fatalf("The resource name must be in the format: 'routeros_[a-z_]+', got '%v'", resName) } resName = fmt.Sprintf("routeros_%v", strings.TrimSuffix(*csvTable, filepath.Ext(*csvTable))) if !reNewItemName.MatchString(resName) { Fatalf("The resource name must be in the format: 'routeros_[a-z_]+', got '%v'", resName) } } var Schema string if *csvTable != "" { if _, err := os.Stat(*csvTable); err != nil { Fatalf("CSV file %v not found", *csvTable) } Schema = extractAttributes(*csvTable) } itemType := Resource if *isDS { itemType = Datasource } itemCrud := "" if *isSystem { itemCrud = "System" } log.Printf("Creating a template for '%v' %v (%v)", resName, color.YellowString(itemType.String()), color.YellowString(itemCrud)) goName := Capitalize(resName) os.MkdirAll("routeros", os.ModePerm) fName := fmt.Sprintf("%v_%v", itemType.HCL(), strings.TrimPrefix(resName, "routeros_")) f, err := os.OpenFile(filepath.Join("routeros", fName+".go"), os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644) if err != nil { panic(err) } // Resource / Datasource var tmpl *template.Template if !*isDS { tmpl, err = template.New("rs_ds").Parse(resourceFile) } else { tmpl, err = template.New("rs_ds").Parse(datasourceFile) } if err != nil { panic(err) } err = tmpl.Execute(f, struct { GoResourceName string System bool Schema string ResourcePath string }{itemType.String() + goName, *isSystem, Schema, strings.ReplaceAll(strings.TrimPrefix(resName, "routeros_"), "_", "/")}) if err != nil { panic(err) } f.Close() f, err = os.OpenFile(filepath.Join("routeros", fName+"_test.go"), os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644) if err != nil { panic(err) } // Test if !*isDS { tmpl, err = template.New("test").Parse(resourceTestFile) } else { tmpl, err = template.New("test").Parse(datasourceTestFile) } if err != nil { panic(err) } err = tmpl.Execute(f, struct { GoResourceName string ResourceName string ResourcePath string System bool }{goName, resName, strings.ReplaceAll(strings.TrimPrefix(resName, "routeros_"), "_", "/"), *isSystem}) if err != nil { panic(err) } f.Close() // Example if !*isDS { os.MkdirAll(filepath.Join("examples", "resources", resName), os.ModePerm) } else { os.MkdirAll(filepath.Join("examples", "data-sources", resName), os.ModePerm) } if !*isDS { f, err = os.OpenFile(filepath.Join("examples", "resources", resName, "import.sh"), os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644) if err != nil { panic(err) } tmpl, err = template.New("ex_import").Parse(exampleImportFile) if err != nil { panic(err) } err = tmpl.Execute(f, struct { ResourceName string ResourcePath string }{resName, strings.ReplaceAll(strings.TrimPrefix(resName, "routeros_"), "_", "/")}) if err != nil { panic(err) } f.Close() f, err = os.OpenFile(filepath.Join("examples", "resources", resName, "resource.tf"), os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644) if err != nil { panic(err) } tmpl, err = template.New("ex_res").Parse(exampleResourceFile) if err != nil { panic(err) } err = tmpl.Execute(f, struct { ResourceName string }{resName}) if err != nil { panic(err) } f.Close() } else { f, err = os.OpenFile(filepath.Join("examples", "data-sources", resName, "data-source.tf"), os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644) if err != nil { panic(err) } _, err = f.WriteString(fmt.Sprintf("data \"%v\" \"data\" {}", resName)) if err != nil { panic(err) } f.Close() } // var flags int = os.O_WRONLY | os.O_APPEND // if _, err := os.Stat(filepath.Join("routeros", "provider.go")); err != nil { // flags |= os.O_CREATE // } f, err = os.OpenFile(filepath.Join("routeros", resName+"_provider.go"), os.O_WRONLY|os.O_CREATE, 0644) if err != nil { panic(err) } fmt.Fprintf(f, "\"%v\": %v(),\n", resName, itemType.String()+goName) f.Close() // } } var exampleImportFile = `#The ID can be found via API or the terminal #The command for the terminal is -> :put [/{{.ResourcePath}} get [print show-ids]] terraform import {{.ResourceName}}.test *3 #Or you can import a resource using one of its attributes terraform import {{.ResourceName}}.test "name=xxx"` var exampleResourceFile = `resource "{{.ResourceName}}" "test" { }` var resourceTestFile = ` package routeros import ( "fmt" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const test{{.GoResourceName}} = "{{.ResourceName}}.test" func TestAcc{{.GoResourceName}}Test_basic(t *testing.T) { t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, CheckDestroy: testCheckResourceDestroy("/{{.ResourcePath}}", "{{.ResourceName}}"), Steps: []resource.TestStep{ { Config: testAcc{{.GoResourceName}}Config({{- if .System }}""{{end}}), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(test{{.GoResourceName}}), resource.TestCheckResourceAttr(test{{.GoResourceName}}, "", ""), ), },{{- if .System }} { Config: testAcc{{.GoResourceName}}Config(""), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(test{{.GoResourceName}}), resource.TestCheckResourceAttr(test{{.GoResourceName}}, "", ""), ), },{{end}} }, }) }) } } func testAcc{{.GoResourceName}}Config({{- if .System }}param string{{end}}) string { return fmt.Sprintf(` + "`" + `%v resource "{{.ResourceName}}" "test" { } ` + "`" + `, providerConfig{{- if .System }}, param{{end}}) } ` var datasourceTestFile = ` package routeros import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) const testDatasource{{.GoResourceName}} = "data.{{.ResourceName}}.data" func TestAccDatasource{{.GoResourceName}}Test_basic(t *testing.T) { t.Parallel() for _, name := range testNames { t.Run(name, func(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) testSetTransportEnv(t, name) }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { Config: testAccDatasource{{.GoResourceName}}Config(), Check: resource.ComposeTestCheckFunc( testResourcePrimaryInstanceId(testDatasource{{.GoResourceName}}), ), }, }, }) }) } } func testAccDatasource{{.GoResourceName}}Config() string { return providerConfig + ` + "`" + ` data "{{.ResourceName}}" "data" {} ` + "`" + ` }` var resourceFile = ` package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* REST JSON */ // https://help.mikrotik.com/docs/display/ROS/ func {{.GoResourceName}}() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/{{.ResourcePath}}"), MetaId: PropId(Id), {{.Schema}} } return &schema.Resource{ CreateContext: Default{{- if .System }}System{{end}}Create(resSchema), ReadContext: Default{{- if .System }}System{{end}}Read(resSchema), UpdateContext: Default{{- if .System }}System{{end}}Update(resSchema), DeleteContext: Default{{- if .System }}System{{end}}Delete(resSchema), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, StateContext: ImportStateCustomContext(resSchema), }, Schema: resSchema, } }` var datasourceFile = ` package routeros import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* REST JSON */ // https://help.mikrotik.com/docs/display/ROS/ func {{.GoResourceName}}() *schema.Resource { resSchema := map[string]*schema.Schema{ MetaResourcePath: PropResourcePath("/{{.ResourcePath}}"), MetaId: PropId(Id), MetaSkipFields: PropSkipFields(), {{.Schema}} } return &schema.Resource{ ReadContext: DefaultSystemDatasourceRead(resSchema), Schema: resSchema, } }` func Capitalize(s string) (res string) { s = strings.TrimPrefix(s, "routeros") var uc = false for _, c := range s { if c == '_' { uc = true continue } if uc { res += string(unicode.ToUpper(c)) } else { res += string(c) } uc = false } return } var attribute = ` "{{.Attribute}}": { Type: schema.Type{{.Type}}, Optional: true, Description: "{{.Description}}", {{- if .Slice }} ValidateFunc: validation.StringInSlice([]string{ "{{.Slice}}" }, false),{{ end }} {{- if .DiffSuppress }} DiffSuppressFunc: AlwaysPresentNotUserProvided,{{ end }} }, ` var ( reCSV = regexp.MustCompile(`(?m)"(.*?)"(?:,|$)`) reAttrName = regexp.MustCompile(`[a-z-0-9]+`) reAttrDefault = regexp.MustCompile(`(?m)Default:?\s*(""|\w+)`) reAttrEnum = regexp.MustCompile(`(?m)\(\s*([\w-| ]+);`) enumReplacer = strings.NewReplacer(" ", "", `"`, "`", "'", "`", "|", `", "`) ) func splitDescription(s string) (res string) { if len(s) == 0 { return } s = string(unicode.ToUpper(rune(s[0]))) + s[1:] if s[len(s)-1] != '.' { s += "." } if len(s) < 86 { return s } var maxLen = 90 var i int for _, c := range s { res += string(c) i++ if c == ' ' && i >= maxLen { res += "\" +\n \"" maxLen = 100 i = 0 } } return } func extractAttributes(filename string) string { tmpl, err := template.New("attr").Parse(attribute) if err != nil { panic(err) } tmpl.Option() file, err := os.Open(filename) if err != nil { Fatalf("[extractAttributes] %v", err) } defer file.Close() ww := bytes.NewBuffer(nil) scanner := bufio.NewScanner(file) for scanner.Scan() { row := scanner.Text() rec := reCSV.FindAllStringSubmatch(row, -1) if len(rec) != 2 { fmt.Fprintln(ww, row) continue } r1, r2 := rec[0][1], rec[1][1] if len(r1) > 0 && r1[0] == '"' { r1 = r1[1:] } if len(r1) > 0 && r1[len(r1)-1] == '"' { r1 = r1[:len(r1)-1] } if len(r2) > 0 && r2[0] == '"' { r2 = r2[1:] } if len(r2) > 0 && r2[len(r2)-1] == '"' { r2 = r2[:len(r2)-1] } // [ ["Property", Property] ["Description" Description] ] if (r1 == "Property" || r1 == "Parameters") && r2 == "Description" { continue } var diffSuppress bool attrType := "String" if res := reAttrDefault.FindStringSubmatch(r1); len(res) > 1 { switch res[1] { // src-address (Default:"") case `""`: // use-network-apn (yes | no; Default: yes) case "yes", "no": attrType = "Bool" diffSuppress = true // startup-delay (Default: 5m) default: diffSuppress = true if _, err := strconv.Atoi(res[1]); err == nil { attrType = "Int" } } } var validate string for _, match := range reAttrEnum.FindAllStringSubmatch(r1, -1) { validate = enumReplacer.Replace(match[1]) } tmpl.Execute(ww, struct { Attribute string Type string Description string Slice string DiffSuppress bool }{ Attribute: strings.ReplaceAll(reAttrName.FindString(r1), "-", "_"), Type: attrType, Description: splitDescription(strings.ReplaceAll(r2, `"`, "`")), Slice: validate, DiffSuppress: diffSuppress, }) if r1 == "type" { return ww.String() } if err != nil { Fatalf("%v", err) } } return ww.String() } ================================================ FILE: tools/drift/main.go ================================================ //go:build ignore // +build ignore package main import ( "log" "os" "text/template" "gopkg.in/yaml.v3" ) type YamlROS map[string]map[string][]struct { TF string MT string } func main() { fileOut, err := os.OpenFile("mikrotik_resource_drift.go", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) if err != nil { log.Fatal(err) } defer fileOut.Close() m := YamlROS{} fileIn, err := os.Open("mikrotik_resource_drift.yaml") if err != nil { log.Fatalf("error: %v", err) } defer fileIn.Close() dec := yaml.NewDecoder(fileIn) err = dec.Decode(&m) driftTemplate.Execute(fileOut, m) } var driftTemplate = template.Must(template.New("").Parse(`// Code generated by "tools/drift/main.go"; DO NOT EDIT. package routeros func init() { {{- range $version, $resource := . }} {{- range $name, $attributes := $resource }} {{- range $attr := $attributes }} {{ printf "driftAttributeSlice.Add(%q, %q, %q, %q)" $version $name $attr.TF $attr.MT }} {{- end }} {{- end }} {{- end }} driftAttributeSlice.SortDesc() } `)) ================================================ FILE: tools/importer/logger.go ================================================ package main import ( "context" "github.com/sirupsen/logrus" ) type ctxLogger struct{} func NewLogger(ctx context.Context) (*logrus.Logger, context.Context) { log := logrus.New() log.SetFormatter(&logrus.TextFormatter{ ForceColors: true, DisableQuote: true, DisableTimestamp: true, }) log.SetLevel(logrus.DebugLevel) // log.SetReportCaller(true) return log, ContextWithLogger(ctx, log) } // ContextWithLogger adds logger to context func ContextWithLogger(ctx context.Context, l *logrus.Logger) context.Context { return context.WithValue(ctx, ctxLogger{}, l) } // LoggerFromContext returns logger from context func LoggerFromContext(ctx context.Context) *logrus.Logger { if l, ok := ctx.Value(ctxLogger{}).(*logrus.Logger); ok { return l } return logrus.StandardLogger() } ================================================ FILE: tools/importer/main.go ================================================ package main import ( "bufio" "bytes" "context" "flag" "fmt" "os" "regexp" "slices" "strings" "syscall" "time" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/sirupsen/logrus" "github.com/terraform-routeros/terraform-provider-routeros/routeros" ) var ( log, _ = NewLogger(context.Background()) reUserResources = regexp.MustCompile(`(?m)^(/.*?)\sadd\s(.*?)[\r\n]+`) // (/interface ethernet) set ([ find default-name=ether1 ] disable-running-check=no) // (/ip service) set (api-ssl) (certificate=ssl) reSystemResources = regexp.MustCompile(`(?m)^(/.*?)\sset\s(?:([\w-]+)\s)?(.*?)[\r\n]+`) reAttributes = regexp.MustCompile(`\S+=(?:[^ "]+|"[^"]+")+`) // .id=*1;available=101;name=dhcp;ranges=192.168.88.100-192.168.88.200;total=101;used=0 reId = regexp.MustCompile(`\.id=(\S+?);`) providerTemplate = `terraform { required_providers { routeros = { source = "terraform-routeros/routeros" version = "~> 1" } } } provider "routeros" { hosturl = "https://%v" username = "%v" # password = "" # env ROS_PASSWORD or MIKROTIK_PASSWORD insecure = true } ` importTemplate = `import { to = "%v.%v" id = "%v" } ` resourceTempate = `resource "%v" "%v" { %v } ` ) func main() { host := flag.String("host", "192.168.180.171", "Mikrotik host") user := flag.String("user", "admin", "Mikrotik user") password := flag.String("password", "", "Mikrotik password") logLevel := flag.String("loglevel", "info", "Log level (debug, info, warn, error, fatal, panic)") flag.Parse() level, err := logrus.ParseLevel(*logLevel) if err != nil { fmt.Println("Invalid log level specified:", err) os.Exit(1) } log.SetLevel(level) if *password == "" { *password = getPassword("Enter Mikrotik password: ") } // Prepare buffer for HCL file resHcl := bytes.NewBufferString(fmt.Sprintf(providerTemplate, *host, *user)) provider := routeros.NewProvider() var providerResources = make(map[string][]string) for k, v := range provider.ResourcesMap { path := v.Schema[routeros.MetaResourcePath].Default.(string) // Don't add aliases if _, ok := providerAliasesMap[k]; ok { continue } providerResources[path] = append(providerResources[path], k) } conn, err := NewSsh(fmt.Sprintf("%v:22", *host), *user, *password) if err != nil { log.Fatal(err) } defer conn.Close() // /interface ethernet set [ find default-name=ether2 ] disable-running-check=no // /interface wireguard add listen-port=1829 mtu=1420 name=wg1 // /interface list add name=list // ... config, err := GetMikrotikConfig(conn) if err != nil { log.Fatal(err) } if len(config) == 0 { log.Fatal("Config is empty") } // ip-pool: N, interface-wireguard: N, ... var hclNames = make(map[string]int) // Bool TF -> MT Replacer mtYesNo := strings.NewReplacer("true", "yes", "false", "no") // /interface wireguard >>> add <<< listen-port=1829 mtu=1420 name=wg1 for _, ss := range reUserResources.FindAllStringSubmatch(config, -1) { // "/interface wireguard", "listen-port=1829 mtu=1420 name=wg1" path, attributes := ss[1], ss[2] // routeros_interface_wireguard hclSection, err := GetResourceSection(hclNames, providerResources, path) if err != nil { log.Error(err) continue } // HCL file hclAttributes, required := GetAttributes(provider, hclSection.ResourceName, attributes) // Import script var id string switch hclSection.ResourceName { case "routeros_interface_bridge_filter": fallthrough case "routeros_ip_address": fallthrough case "routeros_ip_dhcp_server_network": fallthrough case "routeros_ip_firewall_filter", "routeros_ip_firewall_mangle", "routeros_ip_firewall_nat", "routeros_ip_firewall_raw": fallthrough case "routeros_ipv6_firewall_filter", "routeros_ipv6_firewall_mangle", "routeros_ipv6_firewall_nat": fallthrough case "routeros_ip_dns_record": fallthrough case "routeros_ip_route": fallthrough case "routeros_wifi_provisioning": var filter []string filter_list := slices.Clone(hclAttributes) for _, filter_value := range filter_list { // Split key and value filter_split := strings.Split(filter_value, "=") log.Debug("filter_split is ", filter_split) // Remove surnumerous spaces from HCL attributes keys filter_split[0] = strings.ReplaceAll(filter_split[0], " ", "") // Replace true and false with yes and no from HCL attributes values filter_split[1] = strings.TrimSpace(filter_split[1]) // Hack for /ip/dhcp-server-network entries which are comma separated list in Mikrotik if filter_split[0] != "ntp_server" && filter_split[0] != "dns_server" && // Hack for /ip/dns/static entry which is stored as integer in Mikrotik filter_split[0] != "ttl" && // Hack for /ip/route blackhole which have enpty gateway (filter_split[0] != "gateway" || filter_split[1] == "?") && // Hack for /interface/wifi/provisioning where supported-bands and slave-configurations are lists filter_split[0] != "supported_bands" && filter_split[0] != "slave_configurations" { // Format HCLAttributes key and values to comply with Mikrotik syntax and properly rebuild filter_value filter_value = routeros.SnakeToKebab(filter_split[0]) + "=" + mtYesNo.Replace(filter_split[1]) filter = append(filter, filter_value) } } id = GetResourceId(conn, path, filter) default: id = GetResourceId(conn, path, required) } // HCL file fmt.Fprintf(resHcl, importTemplate, hclSection.ResourceName, hclSection.HCLName, id) fmt.Fprintf(resHcl, resourceTempate, hclSection.ResourceName, hclSection.HCLName, strings.Join(hclAttributes, "\n")) } // /interface ethernet >>>set<<< [ find default-name=ether1 ] disable-running-check=no // /ip service set api-ssl certificate=ssl for _, ss := range reSystemResources.FindAllStringSubmatch(config, -1) { // "/interface ethernet", "", "[ find default-name=ether1 ] disable-running-check=no" // "/ip service", "api-ssl", "certificate=ssl" path, name, attributes := ss[1], ss[2], ss[3] if name == "" { name = "." } // routeros_ip_service hclSection, err := GetResourceSection(hclNames, providerResources, path) if err != nil { log.Error(err) continue } switch hclSection.ResourceName { case "routeros_ip_service": // Add the Required attribute attributes += " name=" + name + " numbers=" + name case "routeros_routing_bgp_template": fallthrough case "routeros_system_user_group": attributes += " name=" + name } log.Debug("Attributes for path ", path, " are: ", attributes) hclAttributes, required := GetAttributes(provider, hclSection.ResourceName, attributes) switch hclSection.ResourceName { // Skip folowwing resources as they don't have IDs case "routeros_capsman_manager": fallthrough case "routeros_interface_wifi_capsman": fallthrough case "routeros_ip_cloud": fallthrough case "routeros_ip_dns": fallthrough case "routeros_ip_ssh_server": fallthrough case "routeros_ip_upnp": fallthrough case "routeros_system_clock": fallthrough case "routeros_system_identity": fallthrough case "routeros_system_logging": fallthrough case "routeros_system_ntp_client": fallthrough case "routeros_system_ntp_server": fallthrough case "routeros_system_routerboard_settings": fallthrough case "routeros_tool_bandwidth_server": fallthrough case "routeros_tool_mac_server": fallthrough case "routeros_tool_mac_server_winbox": fallthrough case "routeros_tool_mac_server_ping": fallthrough case "routeros_tool_sniffer": fallthrough case "routeros_wifi_capsman": log.Infof("Skipping resource %v which has no ID", hclSection.ResourceName) continue // Get ID for specific use cases case "routeros_interface_ethernet": fallthrough case "routeros_interface_lte_apn": fallthrough case "routeros_interface_wireless_security_profiles": fallthrough case "routeros_ip_ipsec_proposal": fallthrough case "routeros_ip_service": var filter []string filter_list := slices.Clone(hclAttributes) for _, filter_value := range filter_list { // Split key and value filter_split := strings.Split(filter_value, "=") log.Debug("filter_split is ", filter_split) // Remove surnumerous spaces from HCL attributes keys filter_split[0] = strings.ReplaceAll(filter_split[0], " ", "") // Replace true and false with yes and no from HCL attributes values filter_split[1] = strings.TrimSpace(filter_split[1]) // Hack for /ip service which entries have name and not numbers as on Terraform side if filter_split[0] == "numbers" { filter_split[0] = "name" } // Hack for /ip ipsec proposal where name is default and not "." as on Terraform side if filter_split[0] == "name" && (filter_split[1] == "." || filter_split[1] == "\"?\"") { filter_split[1] = "default" } if filter_split[0] != "factory_name" && // Hack for /ip/service entries with address as list filter_split[0] != "address" && // Hack for /ip ipsec proposal with enc-algorithms as list filter_split[0] != "enc_algorithms" && // Hack for /ip/service entries with unknown port (filter_split[0] != "port" && filter_split[1] != "?") { // Format HCLAttributes key and values to comply with Mikrotik syntax and properly rebuild filter_value filter_value = routeros.SnakeToKebab(filter_split[0]) + "=" + mtYesNo.Replace(filter_split[1]) filter = append(filter, filter_value) } } // Get Id name = GetResourceId(conn, path, filter) // Get ID for default use case default: name = GetResourceId(conn, path, required) } // HCL file fmt.Fprintf(resHcl, importTemplate, hclSection.ResourceName, hclSection.HCLName, name) fmt.Fprintf(resHcl, resourceTempate, hclSection.ResourceName, hclSection.HCLName, strings.Join(hclAttributes, "\n")) } // HCL file baseName := fmt.Sprintf("autoimport-%v", time.Now().Format("20060102-1504")) tfFile, err := os.OpenFile(baseName+".tf", os.O_CREATE|os.O_TRUNC|os.O_RDWR, os.ModePerm) if err != nil { log.Fatal(err) } defer tfFile.Close() _, err = resHcl.WriteTo(tfFile) if err != nil { log.Fatal(err) } log.Info("Complete") } // resource "routeros_bgp_connection" "test" { // } // HCLResource "ResourceName" "HCLName" { // } type HCLResource struct { ResourceName string HCLName string } // The function returns the name of the resource in provider notation and the unique identifier of the resource. func GetResourceSection(hclNames map[string]int, providerResources map[string][]string, path string) (*HCLResource, error) { // /ip pool => /ip/pool path = strings.Replace(path, " ", "/", -1) // /ip/pool => routeros_ip_pool resNames, ok := providerResources[path] if !ok { return nil, fmt.Errorf("no resource was found for '%v' path", path) } if len(resNames) == 0 { return nil, fmt.Errorf("path '%v' exists, but no resource name was found for it", path) } // Several resources can have the same path. resourceName := resNames[0] // routeros_ip_pool => ip-pool hclName := strings.ReplaceAll(strings.TrimPrefix(resourceName, "routeros_"), "_", "-") if !strings.Contains(hclName, "system-") { if _, ok := hclNames[hclName]; !ok { hclNames[hclName] = 1 } else { hclNames[hclName]++ } hclName = fmt.Sprintf("%v-%v", hclName, hclNames[hclName]) } // {"routeros_ip_pool", "ip-pool-N"} return &HCLResource{resourceName, hclName}, nil } // routeros_interface_ethernet default_name=ether1 ===> factory_name=ether1, name=ether1 var TransformMap = map[string][]string{ "routeros_interface_ethernet:default_name": {"factory_name", "name"}, } // The function returns a slice of attributes in HCL format and a slice of Required fields. func GetAttributes(provider *schema.Provider, resourceName, attributes string) (hclAttributes, required []string) { // routeros_interface_ethernet resource := provider.ResourcesMap[resourceName] // factory_name, name var attrRequired = make(map[string]schema.ValueType) for k, v := range resource.Schema { if v.Required { attrRequired[k] = v.Type } } // default-name=ether1 if pairs := reAttributes.FindAllString(attributes, -1); len(pairs) > 0 { for _, p := range pairs { // default-name=ether1 pp := strings.Split(p, "=") // default_name, ether1 attrName, attrValue := routeros.KebabToSnake(pp[0]), pp[1] if attrs, ok := TransformMap[resourceName+":"+attrName]; ok { for _, name := range attrs { // + factory_name=ether1, + name=ether1 pairs = append(pairs, fmt.Sprintf("%v=%v", name, attrValue)) } } } for _, p := range pairs { // default-name=ether1 pp := strings.Split(p, "=") attrName, attrValue := routeros.KebabToSnake(pp[0]), pp[1] // "default_name" : { // Type: schema.TypeString, // Computed: true, // Description: description, // } schemaAttr, ok := resource.Schema[attrName] if !ok { log.Warnf("Attribute '%v' not found for resource '%v' in provider schema", attrName, resourceName) continue } // Computed: true, if schemaAttr.Computed { continue } switch schemaAttr.Type { case schema.TypeString: // key=value => key = "value" if len(attrValue) > 0 && attrValue[0] == '"' { attrValue = attrValue[1:] } if len(attrValue) > 0 && attrValue[len(attrValue)-1] == '"' { attrValue = attrValue[:len(attrValue)-1] } attrValue = `"` + attrValue + `"` case schema.TypeBool: // key=yes => key = true attrValue = routeros.BoolFromMikrotikJSONStr(attrValue) case schema.TypeSet, schema.TypeList: // key=a,b,c => key = ["a", "b", "c"] switch schemaAttr.Elem.(*schema.Schema).Type { case schema.TypeString: attrValue = `["` + strings.Join(strings.Split(attrValue, ","), `", "`) + `"]` default: attrValue = `[` + strings.Join(strings.Split(attrValue, ","), `,`) + `]` } } // Add padding hclAttributes = append(hclAttributes, fmt.Sprintf("%v = %v", attrName, attrValue)) // Remove the Required field from the general list if schemaAttr.Required { required = append(required, p) delete(attrRequired, attrName) } } } // Add all required fields with the error-causing value for attrName, attrType := range attrRequired { var value = "?" if attrType == schema.TypeString { value = `"?"` } hclAttributes = append(hclAttributes, fmt.Sprintf(`%v = %v`, attrName, value)) } return } func getPassword(prompt string) string { fmt.Print(prompt) // Common settings and variables for both stty calls. attrs := syscall.ProcAttr{ Dir: "", Env: []string{}, Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()}, Sys: nil} var ws syscall.WaitStatus // Disable echoing. pid, err := syscall.ForkExec( "/bin/stty", []string{"stty", "-echo"}, &attrs) if err != nil { panic(err) } // Wait for the stty process to complete. _, err = syscall.Wait4(pid, &ws, 0, nil) if err != nil { panic(err) } // Echo is disabled, now grab the data. reader := bufio.NewReader(os.Stdin) text, err := reader.ReadString('\n') if err != nil { panic(err) } // Re-enable echo. pid, err = syscall.ForkExec( "/bin/stty", []string{"stty", "echo"}, &attrs) if err != nil { panic(err) } // Wait for the stty process to complete. _, err = syscall.Wait4(pid, &ws, 0, nil) if err != nil { panic(err) } return strings.TrimSpace(text) } ================================================ FILE: tools/importer/resource_aliases.go ================================================ package main var ( providerAliasesMap = map[string]struct{}{ "routeros_dhcp_client": {}, "routeros_dhcp_client_option": {}, "routeros_dhcp_server": {}, "routeros_dhcp_server_network": {}, "routeros_dhcp_server_lease": {}, "routeros_firewall_addr_list": {}, "routeros_firewall_filter": {}, "routeros_firewall_mangle": {}, "routeros_firewall_nat": {}, "routeros_dns": {}, "routeros_dns_record": {}, "routeros_bridge": {}, "routeros_bridge_mlag": {}, "routeros_bridge_port": {}, "routeros_bridge_vlan": {}, "routeros_gre": {}, "routeros_ipip": {}, "routeros_vlan": {}, "routeros_vrrp": {}, "routeros_wireguard": {}, "routeros_wireguard_peer": {}, "routeros_identity": {}, "routeros_scheduler": {}, } ) ================================================ FILE: tools/importer/ssh.go ================================================ package main import ( "bytes" "fmt" "strings" "time" "golang.org/x/crypto/ssh" ) type SshConnection struct { client *ssh.Client } func NewSsh(host, username, password string) (*SshConnection, error) { // var hostKey ssh.PublicKey // An SSH client is represented with a ClientConn. // // To authenticate with the remote server you must pass at least one // implementation of AuthMethod via the Auth field in ClientConfig, // and provide a HostKeyCallback. config := &ssh.ClientConfig{ User: username, Auth: []ssh.AuthMethod{ ssh.Password(password), }, HostKeyCallback: ssh.InsecureIgnoreHostKey(), //ssh.FixedHostKey(hostKey), Timeout: 10 * time.Second, } client, err := ssh.Dial("tcp", host, config) if err != nil { return nil, fmt.Errorf("failed to dial, %v", err) } conn := &SshConnection{ client: client, } return conn, nil } func (c *SshConnection) Close() error { return c.client.Close() } func (c *SshConnection) Run(cmd string) (string, error) { // Each ClientConn can support multiple interactive sessions, // represented by a Session. session, err := c.client.NewSession() if err != nil { return "", fmt.Errorf("failed to create session, %v", err) } defer session.Close() // Once a Session is created, you can execute a single command on // the remote side using the Run method. var b bytes.Buffer session.Stdout = &b if err := session.Run(cmd); err != nil { return "", fmt.Errorf("failed to run, %v", err.Error()) } return b.String(), nil } func GetMikrotikConfig(conn *SshConnection) (string, error) { return conn.Run("/export terse") } func GetResourceId(conn *SshConnection, path string, requiredFields []string) string { var id string var filter = strings.Join(requiredFields, " ") cmd := fmt.Sprintf(":put [%v get [ find %v ]]", path, filter) log.Debug("Searching id in ", path, " with command: ", cmd) res, err := conn.Run(cmd) if err != nil { log.Error("Error running command: ", err) return "?" } ss := reId.FindStringSubmatch(res) log.Debug("ss is ", ss) if len(ss) == 2 { id = ss[1] log.Info("Found id ", id, " for ", filter) return id } // Let's try with dynamic=no cmd = fmt.Sprintf(":put [%v get [ find %v dynamic=no ]]", path, filter) log.Debug("Searching id in ", path, " with command: ", cmd) res, err = conn.Run(cmd) if err != nil { log.Error("Error running command: ", err) return "?" } ss = reId.FindStringSubmatch(res) log.Debug("ss is ", ss) if len(ss) == 2 { id = ss[1] log.Info("Found id ", id, " for ", filter) return id } if id == "" { log.Warnf("Id not found for %v (filter: %v)", requiredFields, filter) return "?" } return id } ================================================ FILE: tools/schema_changes/README.md ================================================ This utility is designed to quickly compare changes that have occurred in ROS of specific versions. The output can be used for information or to create an issue. ROS schema files are required for the utility to work. You can download the latest versions with the following command: ``` export v=7.19 curl "https://tikoci.github.io/restraml/$v/inspect.json" -o- | gzip > ros-$v.json.gz ``` Startup: ``` cd tools/schema_changes go run . -r 7.18:7.19 -markdown ``` * ```-r``` - ROS versions to be compared (```-r 7.18:7.19```) * ```-f``` - resource path filter (```-f bgp```) * ```-all``` - output all changed resources, not just those present in the provider * ```-markdown``` - output in Markdown format for copying to GitHub ================================================ FILE: tools/schema_changes/main.go ================================================ package main import ( "compress/gzip" "encoding/json" "flag" "fmt" "log" "os" "path" "slices" "sort" "strings" "github.com/fatih/color" godiffpatch "github.com/sourcegraph/go-diff-patch" "github.com/terraform-routeros/terraform-provider-routeros/routeros" ) // https://tikoci.github.io/restraml/$v/inspect.json" -o- | gzip > ros-$v.json.gz type Schema map[string]any var ( versionRange = flag.String("r", "", "Version range <7.18:7.19>") filter = flag.String("f", "", "Filter [/routing/bgp]") all = flag.Bool("all", false, "Show all changes") markdown = flag.Bool("markdown", false, "Markdown") ) func main() { flag.Parse() if *versionRange == "" { log.Fatalln("'-r' must not be empty") } vv := strings.Split(*versionRange, ":") if len(vv) != 2 { log.Fatal("Versions should be specified in 'X:Y' format") } f1, err := os.Open("ros-" + vv[0] + ".json.gz") if err != nil { log.Fatal(err) } defer f1.Close() gz1, err := gzip.NewReader(f1) if err != nil { log.Fatal(err) } defer gz1.Close() f2, err := os.Open("ros-" + vv[1] + ".json.gz") if err != nil { log.Fatal(err) } defer f2.Close() gz2, err := gzip.NewReader(f2) if err != nil { log.Fatal(err) } defer gz2.Close() if *markdown { fmt.Println("```diff") } fmt.Printf("%v <=> %v\n", color.RedString("-"+vv[0]), color.GreenString("+"+vv[1])) var schema = Schema{} err = json.NewDecoder(gz1).Decode(&schema) if err != nil { log.Fatal(err) } var localFilter []string if *filter != "" { localFilter = []string{*filter} } else { localFilter = []string{} for _, v := range routeros.Provider().ResourcesMap { localFilter = append(localFilter, v.Schema[routeros.MetaResourcePath].Default.(string)) } sort.Strings(localFilter) } if *all { localFilter = []string{} } oldSchema := Go(schema, "", localFilter) sort.Strings(oldSchema) oldSchema = slices.Compact(oldSchema) schema = Schema{} err = json.NewDecoder(gz2).Decode(&schema) if err != nil { log.Fatal(err) } newSchema := Go(schema, "", localFilter) sort.Strings(newSchema) newSchema = slices.Compact(newSchema) diff := godiffpatch.GeneratePatch("schema.json", strings.Join(oldSchema, "\n"), strings.Join(newSchema, "\n")) var resource string for _, v := range strings.Split(diff, "\n") { if len(v) < 2 || (v[0] != '-' && v[0] != '+') || v[:2] == "--" || v[:2] == "++" { continue } // +/routing/bgp/template:input.filter-communities ss := strings.SplitN(v, ":", 2) if resource != ss[0][1:] { fmt.Println("\n" + ss[0][1:]) } switch v[0] { case '-': if *markdown { fmt.Printf("-%v\n", color.RedString(ss[1])) } else { fmt.Printf(" %v\n", color.RedString(ss[1])) } case '+': if *markdown { fmt.Printf("+%v\n", color.GreenString(ss[1])) } else { fmt.Printf(" %v\n", color.GreenString(ss[1])) } } resource = ss[0][1:] } if *markdown { fmt.Println("```") } } var ( rr = strings.NewReplacer("/add", "", "/set", "", "/reset", "") ) func filterContains(base string, filter []string) bool { base = rr.Replace(base) for _, v := range filter { if base == v { return true } } return false } func Go(s Schema, base string, filter []string) []string { var res []string for k, v := range s { // if k == "_type" && v == "dir" { // fmt.Printf("%2d %v\n", level, base) // } if k != "_type" && (path.Base(base) == "add" || path.Base(base) == "set" || path.Base(base) == "reset") { if len(filter) > 0 { if filterContains(base, filter) { res = append(res, fmt.Sprintf("%v:%v", path.Dir(base), k)) } } else { res = append(res, fmt.Sprintf("%v:%v", path.Dir(base), k)) } } switch vv := v.(type) { case map[string]any: res = append(res, Go(vv, base+"/"+k, filter)...) default: } } return res } ================================================ FILE: tools/tools.go ================================================ //go:build tools // +build tools package tools import ( _ "github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs" )